This commit is contained in:
Domipoke
2026-06-25 19:58:23 +02:00
parent 372c622025
commit 955801c666
147 changed files with 18829 additions and 0 deletions
+58
View File
@@ -0,0 +1,58 @@
import Quickshell
import qs.Commons
import qs.Services.UI
import qs.Widgets
NIconButton {
id: root
property var cfg: pluginApi?.pluginSettings || ({})
property var defaults: pluginApi?.manifest?.metadata?.defaultSettings || ({})
readonly property string iconColorKey: cfg.iconColor ?? defaults.iconColor ?? "none"
property var pluginApi: null
property ShellScreen screen
property string section: ""
property int sectionWidgetIndex: -1
property int sectionWidgetsCount: 0
property string widgetId: ""
baseSize: Style.getCapsuleHeightForScreen(screen?.name)
border.color: Style.capsuleBorderColor
border.width: Style.capsuleBorderWidth
colorBg: Style.capsuleColor
colorFg: Color.resolveColorKey(iconColorKey)
customRadius: Style.radiusL
icon: "cards"
tooltipDirection: BarService.getTooltipDirection(screen?.name)
tooltipText: pluginApi?.tr("widget.tooltip")
onClicked: {
if (pluginApi) {
root.pluginApi?.mainInstance.toggle();
}
}
onRightClicked: {
PanelService.showContextMenu(contextMenu, root, screen);
}
NPopupContextMenu {
id: contextMenu
model: [
{
"label": I18n.tr("actions.widget-settings"),
"action": "widget-settings",
"icon": "settings"
},
]
onTriggered: action => {
contextMenu.close();
PanelService.closeContextMenu(screen);
if (action === "widget-settings") {
BarService.openPluginSettings(screen, pluginApi.manifest);
}
}
}
}
+54
View File
@@ -0,0 +1,54 @@
import QtQuick
import Quickshell.Io
Item {
id: root
property var pluginApi: null
function hide() {
if (windowLoader.item)
windowLoader.item.close();
else
windowLoader.active = false;
}
function show() {
windowLoader.active = true;
}
function toggle() {
if (windowLoader.active)
hide();
else
show();
}
Loader {
id: windowLoader
active: false
source: "Wallcards.qml"
onLoaded: item.pluginApi = Qt.binding(() => root.pluginApi)
Connections {
function onQuitRequested() {
windowLoader.active = false;
}
target: windowLoader.item
}
}
IpcHandler {
function hide() {
root.hide();
}
function show() {
root.show();
}
function toggle() {
root.toggle();
}
target: "plugin:wallcards"
}
}
+135
View File
@@ -0,0 +1,135 @@
# Wallcards
A `lively` wallpaper selector for images and videos with live preview.
> [!NOTE]
> Heavily inspired by other work — see Credits for details.
https://github.com/user-attachments/assets/9ffbc83d-95e5-4dcd-a834-7bd224211b55
## Features
- Browse image and video wallpapers as a scrollable card stack
- Live preview — applies the wallpaper and updates the colorscheme as you navigate
- Filter by type or by dominant color
- Keyboard and partial mouse navigation
- Full settings panel with layout and behavior options
## Dependencies
```sh
pacman -S ffmpeg mpvpaper imagemagick
```
## IPC Commands
Control the plugin from the command line:
```sh
qs -c noctalia-shell ipc call plugin:wallcards toggle
```
## Keybinding Examples
Add to your compositor configuration:
### Hyprland
```conf
bind = SUPER, A, exec, qs -c noctalia-shell ipc call plugin:wallcards toggle
```
### Keybinds
#### Navigation
| Key | Action |
| --- | --- |
| `J` / `←` | Previous wallpaper |
| `K` / `→` | Next wallpaper |
| `H` | Scroll page back |
| `L` | Scroll page forward |
| `R` | Shuffle |
#### Actions
| Key | Action |
| --- | --- |
| `Enter` | Apply wallpaper and close |
| `Space` / `↓` | Apply wallpaper |
| `Esc` / `Q` | Close |
#### Filters
| Key | Action |
| --- | --- |
| `A` | Show all |
| `I` | Filter images |
| `V` | Filter videos |
| `F` | Toggle color filter (based on current card) |
#### View
| Key | Action |
| --- | --- |
| `T` | Toggle top bar |
| `P` | Toggle live preview |
| `?` | Toggle shortcut panel |
#### Layout
| Key | Action |
| --- | --- |
| `Shift + H / L` | Adjust card height |
| `Shift + J / K` | Adjust center card width |
| `Shift + N / P` | Adjust number of visible cards |
| `Ctrl + J / K` | Adjust card spacing |
| `Ctrl + H / L` | Adjust side strip width |
#### Save
| Key | Action |
| --- | --- |
| `Ctrl + S` | Save current layout and view settings |
Scroll wheel also works for navigation.
## Configuration
All settings are available through the plugin settings panel in Noctalia. They can also be edited directly in `settings.json` in the plugin directory `~/.config/noctalia/plugins/wallcards`.
| Setting | Description |
| --- | --- |
| `animation_cards_duration` | Duration of card transition animations in ms |
| `animation_window_duration` | Duration of open/close animations in ms |
| `animate_window` | Enable or disable open/close animations |
| `background_color` | Color of the dimmed backdrop |
| `background_opacity` | Opacity of the dimmed backdrop |
| `cards_shown` | Number of visible cards in the stack |
| `card_height` | Height of the card area in pixels |
| `card_spacing` | Gap between cards in pixels |
| `card_strip_width` | Width of non-center cards |
| `card_radius` | Border radius of the cards |
| `center_width_ratio` | Width of the center card as a ratio of the screen |
| `image_filter` | File extensions treated as images |
| `video_filter` | File extensions treated as videos |
| `hide_help` | Hide the keyboard shortcuts panel |
| `hide_top_bar` | Hide the toolbar above the cards |
| `live_preview` | Apply wallpaper while navigating |
| `selected_filter` | Default filter on open (`all`, `images`, `videos`) |
| `shear_factor` | Shearing applied to the card stack |
| `top_bar_height` | Height of the toolbar |
## Known Issues
- When a video card is played, the following warning is spamed ` WARN: vaExportSurfaceHandle failed`. Please contact me, if you have a solution for this.
- Performance for going left or right differs. It is also affected by the total number of cards (is on my todo list)
## License
MIT License - see repository for details.
## Credits
- Inspired by [ilyamiro](https://github.com/ilyamiro/nixos-configuration) and [liixini](https://github.com/liixini/skwd)
- Built for [Noctalia Shell](https://github.com/noctalia-dev/noctalia-shell)
+360
View File
@@ -0,0 +1,360 @@
import qs.Commons
import qs.Widgets
import QtQuick
import QtQuick.Layouts
import Quickshell
ColumnLayout {
id: root
property int editAnimationCardsDuration: pluginApi?.pluginSettings?.animation_cards_duration ?? pluginApi?.manifest?.metadata?.defaultSettings?.animation_cards_duration
property int editAnimationDuration: pluginApi?.pluginSettings?.animation_duration ?? pluginApi?.manifest?.metadata?.defaultSettings?.animation_window_duration
property int editAnimationWindowDuration: pluginApi?.pluginSettings?.animation_window_duration ?? pluginApi?.manifest?.metadata?.defaultSettings?.animation_window_duration
property color editBackgroundColor: pluginApi?.pluginSettings?.background_color ?? pluginApi?.manifest?.metadata?.defaultSettings?.background_color
property real editBackgroundOpacity: pluginApi?.pluginSettings?.background_opacity ?? pluginApi?.manifest?.metadata?.defaultSettings?.background_opacity
property int editCardHeight: pluginApi?.pluginSettings?.card_height ?? pluginApi?.manifest?.metadata?.defaultSettings?.card_height
property int editCardSpacing: pluginApi?.pluginSettings?.card_spacing ?? pluginApi?.manifest?.metadata?.defaultSettings?.card_spacing
property int editCardStripWidth: pluginApi?.pluginSettings?.card_strip_width ?? pluginApi?.manifest?.metadata?.defaultSettings?.card_strip_width
property int editCardsShown: pluginApi?.pluginSettings?.cards_shown ?? pluginApi?.manifest?.metadata?.defaultSettings?.cards_shown
property real editCenterWidthRatio: pluginApi?.pluginSettings?.center_width_ratio ?? pluginApi?.manifest?.metadata?.defaultSettings?.center_width_ratio
property bool editHideHelp: pluginApi?.pluginSettings?.hide_help ?? pluginApi?.manifest?.metadata?.defaultSettings?.hide_help
property bool editHideTopBar: pluginApi?.pluginSettings?.hide_top_bar ?? pluginApi?.manifest?.metadata?.defaultSettings?.hide_top_bar ?? false
property bool editAnimateWindow: pluginApi?.pluginSettings?.animate_window ?? pluginApi?.manifest?.metadata?.defaultSettings?.animate_window ?? true
property string editIconColor: pluginApi?.pluginSettings?.icon_color ?? "none"
property bool editLivePreview: pluginApi?.pluginSettings?.live_preview ?? pluginApi?.manifest?.metadata?.defaultSettings?.live_preview
property string editSelectedFilter: pluginApi?.pluginSettings?.selected_filter || pluginApi?.manifest?.metadata?.defaultSettings?.selected_filter
property real editShearFactor: pluginApi?.pluginSettings?.shear_factor ?? pluginApi?.manifest?.metadata?.defaultSettings?.shear_factor
property int editTopBarHeight: pluginApi?.pluginSettings?.top_bar_height ?? pluginApi?.manifest?.metadata?.defaultSettings?.top_bar_height
property var pluginApi: null
function saveSettings() {
if (!pluginApi || !pluginApi.pluginSettings) {
Logger.e("Wallcards", "Cannot save: pluginApi or pluginSettings is null");
return;
}
pluginApi.pluginSettings.animation_cards_duration = root.editAnimationCardsDuration;
pluginApi.pluginSettings.animation_window_duration = root.editAnimationWindowDuration;
pluginApi.pluginSettings.background_color = root.editBackgroundColor.toString();
pluginApi.pluginSettings.background_opacity = root.editBackgroundOpacity;
pluginApi.pluginSettings.center_width_ratio = root.editCenterWidthRatio;
pluginApi.pluginSettings.card_height = root.editCardHeight;
pluginApi.pluginSettings.card_spacing = root.editCardSpacing;
pluginApi.pluginSettings.card_strip_width = root.editCardStripWidth;
pluginApi.pluginSettings.cards_shown = root.editCardsShown;
pluginApi.pluginSettings.shear_factor = root.editShearFactor;
pluginApi.pluginSettings.top_bar_height = root.editTopBarHeight;
pluginApi.pluginSettings.iconColor = root.editIconColor;
pluginApi.pluginSettings.selected_filter = root.editSelectedFilter;
pluginApi.pluginSettings.live_preview = root.editLivePreview;
pluginApi.pluginSettings.hide_help = root.editHideHelp;
pluginApi.pluginSettings.hide_top_bar = root.editHideTopBar;
pluginApi.pluginSettings.animate_window = root.editAnimateWindow;
pluginApi.saveSettings();
Logger.i("Wallcards", "Settings saved");
}
Layout.rightMargin: Style.marginL
spacing: Style.marginL
// ── Icon ──
NComboBox {
currentKey: root.editIconColor
description: I18n.tr("common.select-color-description")
label: I18n.tr("common.select-icon-color")
minimumWidth: 200
model: Color.colorKeyModel
onSelected: key => root.editIconColor = key
}
NDivider {
Layout.fillWidth: true
}
// ── Behavior ──
NToggle {
checked: root.editLivePreview
defaultValue: pluginApi?.manifest?.metadata?.defaultSettings?.live_preview ?? false
description: root.pluginApi?.tr("settings.live-preview.description")
label: root.pluginApi?.tr("settings.live-preview.label")
onToggled: c => root.editLivePreview = c
}
NComboBox {
currentKey: root.editSelectedFilter
defaultValue: pluginApi?.manifest?.metadata?.defaultSettings?.selected_filter || "all"
description: root.pluginApi?.tr("settings.default-filter.description")
label: root.pluginApi?.tr("settings.default-filter.label")
model: [
{
"key": "all",
"name": root.pluginApi?.tr("buttons.all")
},
{
"key": "images",
"name": root.pluginApi?.tr("buttons.images")
},
{
"key": "videos",
"name": root.pluginApi?.tr("buttons.videos")
}
]
onSelected: key => root.editSelectedFilter = key
}
NToggle {
checked: root.editHideHelp
defaultValue: pluginApi?.manifest?.metadata?.defaultSettings?.hide_help ?? false
description: root.pluginApi?.tr("settings.hide-shortcuts.description")
label: root.pluginApi?.tr("settings.hide-shortcuts.label")
onToggled: c => root.editHideHelp = c
}
NToggle {
checked: root.editHideTopBar
defaultValue: pluginApi?.manifest?.metadata?.defaultSettings?.hide_top_bar ?? false
description: root.pluginApi?.tr("settings.hide-top-bar.description")
label: root.pluginApi?.tr("settings.hide-top-bar.label")
onToggled: c => root.editHideTopBar = c
}
NDivider {
Layout.fillWidth: true
}
// ── Card Layout ──
ColumnLayout {
Layout.fillWidth: true
spacing: Style.marginXXS
NLabel {
description: root.pluginApi?.tr("settings.center-card-width.description")
label: root.pluginApi?.tr("settings.center-card-width.label")
}
NValueSlider {
Layout.fillWidth: true
from: 0.20
stepSize: 0.01
text: (root.editCenterWidthRatio * 100).toFixed(0) + "%"
to: 0.60
value: root.editCenterWidthRatio
onMoved: value => root.editCenterWidthRatio = value
}
}
ColumnLayout {
Layout.fillWidth: true
spacing: Style.marginXXS
NLabel {
description: root.pluginApi?.tr("settings.card-height.description")
label: root.pluginApi?.tr("settings.card-height.label")
}
NValueSlider {
Layout.fillWidth: true
from: 100
stepSize: 10
text: root.editCardHeight + "px"
to: 800
value: root.editCardHeight
onMoved: value => root.editCardHeight = value
}
}
ColumnLayout {
Layout.fillWidth: true
spacing: Style.marginXXS
NLabel {
description: root.pluginApi?.tr("settings.strip-width.description")
label: root.pluginApi?.tr("settings.strip-width.label")
}
NValueSlider {
Layout.fillWidth: true
from: 20
stepSize: 5
text: root.editCardStripWidth + "px"
to: 150
value: root.editCardStripWidth
onMoved: value => root.editCardStripWidth = value
}
}
ColumnLayout {
Layout.fillWidth: true
spacing: Style.marginXXS
NLabel {
description: root.pluginApi?.tr("settings.card-spacing.description")
label: root.pluginApi?.tr("settings.card-spacing.label")
}
NValueSlider {
Layout.fillWidth: true
from: 0
stepSize: 1
text: root.editCardSpacing + "px"
to: 50
value: root.editCardSpacing
onMoved: value => root.editCardSpacing = value
}
}
ColumnLayout {
Layout.fillWidth: true
spacing: Style.marginXXS
NLabel {
description: root.pluginApi?.tr("settings.cards-shown.description")
label: root.pluginApi?.tr("settings.cards-shown.label")
}
NValueSlider {
Layout.fillWidth: true
from: 5
stepSize: 2
text: root.editCardsShown
to: 15
value: root.editCardsShown
onMoved: value => root.editCardsShown = value
}
}
ColumnLayout {
Layout.fillWidth: true
spacing: Style.marginXXS
ColumnLayout {
Layout.fillWidth: true
spacing: Style.marginXXS
NLabel {
description: root.pluginApi?.tr("settings.shear-factor.description")
label: root.pluginApi?.tr("settings.shear-factor.label")
}
NValueSlider {
Layout.fillWidth: true
from: -0.3
stepSize: 0.01
text: root.editShearFactor.toFixed(2)
to: 0.3
value: root.editShearFactor
onMoved: value => root.editShearFactor = value
}
}
}
ColumnLayout {
Layout.fillWidth: true
spacing: Style.marginXXS
NLabel {
description: root.pluginApi?.tr("settings.card-animation.description")
label: root.pluginApi?.tr("settings.card-animation.label")
}
NValueSlider {
Layout.fillWidth: true
from: 0
stepSize: 100
text: root.editAnimationCardsDuration + "ms"
to: 1500
value: root.editAnimationCardsDuration
onMoved: value => root.editAnimationCardsDuration = value
}
}
NDivider {
Layout.fillWidth: true
}
// ── Appearance ──
RowLayout {
NLabel {
Layout.alignment: Qt.AlignTop
description: root.pluginApi?.tr("settings.background-color.description")
label: root.pluginApi?.tr("settings.background-color.label")
}
NColorPicker {
selectedColor: root.editBackgroundColor
onColorSelected: color => root.editBackgroundColor = color
}
}
ColumnLayout {
Layout.fillWidth: true
spacing: Style.marginXXS
NLabel {
description: root.pluginApi?.tr("settings.background-opacity.description")
label: root.pluginApi?.tr("settings.background-opacity.label")
}
NValueSlider {
Layout.fillWidth: true
from: 0
stepSize: 0.05
text: (root.editBackgroundOpacity * 100).toFixed(0) + "%"
to: 1
value: root.editBackgroundOpacity
onMoved: value => root.editBackgroundOpacity = value
}
}
ColumnLayout {
Layout.fillWidth: true
spacing: Style.marginXXS
NLabel {
description: root.pluginApi?.tr("settings.window-animation-speed.description")
label: root.pluginApi?.tr("settings.window-animation-speed.label")
}
NValueSlider {
Layout.fillWidth: true
from: 0
stepSize: 100
text: root.editAnimationWindowDuration + "ms"
to: 1500
value: root.editAnimationWindowDuration
onMoved: value => root.editAnimationWindowDuration = value
}
}
NToggle {
checked: root.editAnimateWindow
defaultValue: pluginApi?.manifest?.metadata?.defaultSettings?.animate_window ?? true
description: root.pluginApi?.tr("settings.animate-window.description")
label: root.pluginApi?.tr("settings.animate-window.label")
onToggled: c => root.editAnimateWindow = c
}
NDivider {
Layout.fillWidth: true
}
// ── Top Bar ──
ColumnLayout {
Layout.fillWidth: true
spacing: Style.marginXXS
NLabel {
description: root.pluginApi?.tr("settings.top-bar-height.description")
label: root.pluginApi?.tr("settings.top-bar-height.label")
}
NValueSlider {
Layout.fillWidth: true
from: 10
stepSize: 2
text: root.editTopBarHeight + "px"
to: 80
value: root.editTopBarHeight
onMoved: value => root.editTopBarHeight = value
}
}
ColumnLayout {
Layout.fillWidth: true
spacing: Style.marginXXS
}
}
+414
View File
@@ -0,0 +1,414 @@
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()
}
}
}
+116
View File
@@ -0,0 +1,116 @@
{
"buttons": {
"all": "Alle",
"images": "Bilder",
"videos": "Videos",
"shuffle": "Mischen",
"live-preview": "Live",
"color-na": "✕"
},
"widget": {
"tooltip": "Wallcards öffnen",
"generate-thumbs-message": "Miniaturbilder werden erstellt…"
},
"settings": {
"icon-color": {
"label": "Symbolfarbe",
"description": "Farbe des Leistensymbols"
},
"live-preview": {
"label": "Live-Vorschau",
"description": "Hintergrundbild beim Durchblättern anwenden"
},
"default-filter": {
"label": "Standardfilter",
"description": "Anfänglicher Dateitypfilter beim Öffnen"
},
"hide-shortcuts": {
"label": "Tastenkürzel ausblenden",
"description": "Tastenkürzel-Leiste ausblenden"
},
"hide-top-bar": {
"label": "Kopfleiste ausblenden",
"description": "Werkzeugleiste über den Karten ausblenden"
},
"center-card-width": {
"label": "Breite der mittleren Karte",
"description": "Breite der mittleren Karte in Prozent des Bildschirms"
},
"card-height": {
"label": "Kartenhöhe",
"description": "Höhe der Hintergrundbilder-Karten"
},
"strip-width": {
"label": "Streifenbreite",
"description": "Breite der seitlichen Kartenstreifen"
},
"card-spacing": {
"label": "Kartenabstand",
"description": "Abstand zwischen den Karten"
},
"cards-shown": {
"label": "Angezeigte Karten",
"description": "Anzahl sichtbarer Karten (ungerade Zahlen empfohlen)"
},
"shear-factor": {
"label": "Scherungsfaktor",
"description": "Neigungswinkel des Kartenlayouts"
},
"card-animation": {
"label": "Kartenanimation",
"description": "Geschwindigkeit der Kartenübergänge"
},
"background-color": {
"label": "Hintergrundfarbe",
"description": "Farbe der abgedunkelten Überblendung"
},
"background-opacity": {
"label": "Hintergrund-Deckkraft",
"description": "Deckkraft der abgedunkelten Überblendung"
},
"window-animation-speed": {
"label": "Fensteranimation-Geschwindigkeit",
"description": "Geschwindigkeit der Öffnen- und Schließen-Übergänge"
},
"animate-window": {
"label": "Fensteranimation",
"description": "Öffnen- und Schließen-Übergänge animieren"
},
"top-bar-height": {
"label": "Kopfleistenhöhe",
"description": "Höhe der Werkzeugleiste"
}
},
"shortcuts": {
"key-separator": "/",
"header": {
"navigation": "NAVIGATION",
"actions": "AKTIONEN",
"filters": "FILTER",
"view": "ANSICHT",
"layout": "LAYOUT"
},
"label": {
"help-title": "Tastenkürzel",
"navigate": "Navigieren",
"jump": "Springen",
"shuffle": "Mischen",
"apply-quit": "Anwenden + Schließen",
"apply": "Anwenden",
"quit": "Schließen",
"filter-all": "Alle",
"filter-images": "Bilder",
"filter-videos": "Videos",
"filter-colors": "Farbfilter",
"top-bar": "Kopfleiste umschalten",
"live-preview": "Live-Vorschau umschalten",
"center-height": "Kartenhöhe",
"center-width": "Kartenbreite",
"cards-shown": "Angezeigte Karten",
"cards-spacing": "Abstand",
"cards-width": "Kartenbreite",
"save": "Einstellungen speichern",
"hide": "Ausblenden"
}
}
}
+116
View File
@@ -0,0 +1,116 @@
{
"buttons": {
"all": "All",
"images": "Images",
"videos": "Videos",
"shuffle": "Shuffle",
"live-preview": "Live",
"color-na": "✕"
},
"widget": {
"tooltip": "Open Wallcards",
"generate-thumbs-message": "Generating thumbnails…"
},
"settings": {
"icon-color": {
"label": "Icon Color",
"description": "Color of the bar widget icon"
},
"live-preview": {
"label": "Live Preview",
"description": "Apply wallpaper while browsing cards"
},
"default-filter": {
"label": "Default Filter",
"description": "Initial file type filter when opening"
},
"hide-shortcuts": {
"label": "Hide Shortcuts",
"description": "Hide the keyboard shortcuts panel"
},
"hide-top-bar": {
"label": "Hide Top Bar",
"description": "Hide the toolbar above the cards"
},
"center-card-width": {
"label": "Center Card Width",
"description": "Width of the center card as percentage of screen"
},
"card-height": {
"label": "Card Height",
"description": "Height of the wallpaper cards"
},
"strip-width": {
"label": "Strip Width",
"description": "Width of the side card strips"
},
"card-spacing": {
"label": "Card Spacing",
"description": "Gap between cards"
},
"cards-shown": {
"label": "Cards Shown",
"description": "Number of visible cards (odd numbers recommended)"
},
"shear-factor": {
"label": "Shear Factor",
"description": "Skew angle of the card layout"
},
"card-animation": {
"label": "Card Animation",
"description": "Speed of card transitions"
},
"background-color": {
"label": "Background Color",
"description": "Color of the dimmed overlay"
},
"background-opacity": {
"label": "Background Opacity",
"description": "Opacity of the dimmed overlay"
},
"window-animation-speed": {
"label": "Window Animation Speed",
"description": "Speed of open and close transitions"
},
"animate-window": {
"label": "Window Animation",
"description": "Animate the open and close transitions"
},
"top-bar-height": {
"label": "Top Bar Height",
"description": "Height of the toolbar"
}
},
"shortcuts": {
"key-separator": "/",
"header": {
"navigation": "NAVIGATION",
"actions": "ACTIONS",
"filters": "FILTERS",
"view": "VIEW",
"layout": "LAYOUT"
},
"label": {
"help-title": "Shortcuts",
"navigate": "Navigate",
"jump": "Jump",
"shuffle": "Shuffle",
"apply-quit": "Apply + Quit",
"apply": "Apply",
"quit": "Quit",
"filter-all": "All",
"filter-images": "Images",
"filter-videos": "Videos",
"filter-colors": "Color Filter",
"top-bar": "Toggle Top Bar",
"live-preview": "Toggle Live Preview",
"center-height": "Center Height",
"center-width": "Center Width",
"cards-shown": "Cards Shown",
"cards-spacing": "Spacing",
"cards-width": "Cards Width",
"save": "Save Settings",
"hide": "Hide"
}
}
}
+116
View File
@@ -0,0 +1,116 @@
{
"buttons": {
"all": "Todos",
"images": "Imágenes",
"videos": "Vídeos",
"shuffle": "Mezclar",
"live-preview": "En vivo",
"color-na": "✕"
},
"widget": {
"tooltip": "Abrir Wallcards",
"generate-thumbs-message": "Generando miniaturas…"
},
"settings": {
"icon-color": {
"label": "Color del icono",
"description": "Color del icono del widget de barra"
},
"live-preview": {
"label": "Vista previa en vivo",
"description": "Aplicar fondo mientras navegas por las tarjetas"
},
"default-filter": {
"label": "Filtro predeterminado",
"description": "Filtro de tipo de archivo inicial al abrir"
},
"hide-shortcuts": {
"label": "Ocultar atajos",
"description": "Ocultar el panel de atajos de teclado"
},
"hide-top-bar": {
"label": "Ocultar barra superior",
"description": "Ocultar la barra de herramientas sobre las tarjetas"
},
"center-card-width": {
"label": "Ancho de tarjeta central",
"description": "Ancho de la tarjeta central como porcentaje de la pantalla"
},
"card-height": {
"label": "Altura de tarjeta",
"description": "Altura de las tarjetas de fondo"
},
"strip-width": {
"label": "Ancho de franja",
"description": "Ancho de las franjas laterales"
},
"card-spacing": {
"label": "Espaciado de tarjetas",
"description": "Espacio entre tarjetas"
},
"cards-shown": {
"label": "Tarjetas visibles",
"description": "Número de tarjetas visibles (se recomiendan números impares)"
},
"shear-factor": {
"label": "Factor de inclinación",
"description": "Ángulo de inclinación del diseño de tarjetas"
},
"card-animation": {
"label": "Animación de tarjetas",
"description": "Velocidad de las transiciones de tarjetas"
},
"background-color": {
"label": "Color de fondo",
"description": "Color de la superposición oscurecida"
},
"background-opacity": {
"label": "Opacidad del fondo",
"description": "Opacidad de la superposición oscurecida"
},
"window-animation-speed": {
"label": "Velocidad de animación de ventana",
"description": "Velocidad de las transiciones de abrir y cerrar"
},
"animate-window": {
"label": "Animación de ventana",
"description": "Animar las transiciones de abrir y cerrar"
},
"top-bar-height": {
"label": "Altura de barra superior",
"description": "Altura de la barra de herramientas"
}
},
"shortcuts": {
"key-separator": "/",
"header": {
"navigation": "NAVEGACIÓN",
"actions": "ACCIONES",
"filters": "FILTROS",
"view": "VISTA",
"layout": "DISEÑO"
},
"label": {
"help-title": "Atajos",
"navigate": "Navegar",
"jump": "Saltar",
"shuffle": "Mezclar",
"apply-quit": "Aplicar + Cerrar",
"apply": "Aplicar",
"quit": "Cerrar",
"filter-all": "Todos",
"filter-images": "Imágenes",
"filter-videos": "Vídeos",
"filter-colors": "Filtro de color",
"top-bar": "Barra superior",
"live-preview": "Vista previa",
"center-height": "Altura central",
"center-width": "Ancho central",
"cards-shown": "Tarjetas visibles",
"cards-spacing": "Espaciado",
"cards-width": "Ancho de tarjetas",
"save": "Guardar ajustes",
"hide": "Ocultar"
}
}
}
+116
View File
@@ -0,0 +1,116 @@
{
"buttons": {
"all": "Tous",
"images": "Images",
"videos": "Vidéos",
"shuffle": "Mélanger",
"live-preview": "En direct",
"color-na": "✕"
},
"widget": {
"tooltip": "Ouvrir Wallcards",
"generate-thumbs-message": "Génération des miniatures…"
},
"settings": {
"icon-color": {
"label": "Couleur de l'icône",
"description": "Couleur de l'icône du widget de barre"
},
"live-preview": {
"label": "Aperçu en direct",
"description": "Appliquer le fond d'écran en parcourant les cartes"
},
"default-filter": {
"label": "Filtre par défaut",
"description": "Filtre de type de fichier initial à l'ouverture"
},
"hide-shortcuts": {
"label": "Masquer les raccourcis",
"description": "Masquer le panneau des raccourcis clavier"
},
"hide-top-bar": {
"label": "Masquer la barre supérieure",
"description": "Masquer la barre d'outils au-dessus des cartes"
},
"center-card-width": {
"label": "Largeur de la carte centrale",
"description": "Largeur de la carte centrale en pourcentage de l'écran"
},
"card-height": {
"label": "Hauteur des cartes",
"description": "Hauteur des cartes de fond d'écran"
},
"strip-width": {
"label": "Largeur des bandes",
"description": "Largeur des bandes latérales"
},
"card-spacing": {
"label": "Espacement des cartes",
"description": "Espace entre les cartes"
},
"cards-shown": {
"label": "Cartes affichées",
"description": "Nombre de cartes visibles (nombres impairs recommandés)"
},
"shear-factor": {
"label": "Facteur de cisaillement",
"description": "Angle d'inclinaison de la disposition des cartes"
},
"card-animation": {
"label": "Animation des cartes",
"description": "Vitesse des transitions de cartes"
},
"background-color": {
"label": "Couleur de fond",
"description": "Couleur de la superposition assombrie"
},
"background-opacity": {
"label": "Opacité du fond",
"description": "Opacité de la superposition assombrie"
},
"window-animation-speed": {
"label": "Vitesse d'animation de la fenêtre",
"description": "Vitesse des transitions d'ouverture et de fermeture"
},
"animate-window": {
"label": "Animation de la fenêtre",
"description": "Animer les transitions d'ouverture et de fermeture"
},
"top-bar-height": {
"label": "Hauteur de la barre supérieure",
"description": "Hauteur de la barre d'outils"
}
},
"shortcuts": {
"key-separator": "/",
"header": {
"navigation": "NAVIGATION",
"actions": "ACTIONS",
"filters": "FILTRES",
"view": "AFFICHAGE",
"layout": "DISPOSITION"
},
"label": {
"help-title": "Raccourcis",
"navigate": "Naviguer",
"jump": "Sauter",
"shuffle": "Mélanger",
"apply-quit": "Appliquer + Quitter",
"apply": "Appliquer",
"quit": "Quitter",
"filter-all": "Tous",
"filter-images": "Images",
"filter-videos": "Vidéos",
"filter-colors": "Filtre couleur",
"top-bar": "Barre supérieure",
"live-preview": "Aperçu en direct",
"center-height": "Hauteur centrale",
"center-width": "Largeur centrale",
"cards-shown": "Cartes affichées",
"cards-spacing": "Espacement",
"cards-width": "Largeur des cartes",
"save": "Enregistrer",
"hide": "Masquer"
}
}
}
+116
View File
@@ -0,0 +1,116 @@
{
"buttons": {
"all": "Összes",
"images": "Képek",
"videos": "Videók",
"shuffle": "Keverés",
"live-preview": "Élő",
"color-na": "✕"
},
"widget": {
"tooltip": "Wallcards megnyitása",
"generate-thumbs-message": "Miniatűrök létrehozása…"
},
"settings": {
"icon-color": {
"label": "Ikon színe",
"description": "A sáv widget ikonjának színe"
},
"live-preview": {
"label": "Élő előnézet",
"description": "Háttérkép alkalmazása böngészés közben"
},
"default-filter": {
"label": "Alapértelmezett szűrő",
"description": "Kezdeti fájltípus-szűrő megnyitáskor"
},
"hide-shortcuts": {
"label": "Gyorsbillentyűk elrejtése",
"description": "A gyorsbillentyűk panel elrejtése"
},
"hide-top-bar": {
"label": "Felső sáv elrejtése",
"description": "Az eszköztár elrejtése a kártyák felett"
},
"center-card-width": {
"label": "Középső kártya szélessége",
"description": "A középső kártya szélessége a képernyő százalékában"
},
"card-height": {
"label": "Kártya magassága",
"description": "A háttérkép-kártyák magassága"
},
"strip-width": {
"label": "Csík szélessége",
"description": "Az oldalsó kártyacsíkok szélessége"
},
"card-spacing": {
"label": "Kártyák térköze",
"description": "Kártyák közötti rés"
},
"cards-shown": {
"label": "Megjelenített kártyák",
"description": "Látható kártyák száma (páratlan számok ajánlottak)"
},
"shear-factor": {
"label": "Nyírási tényező",
"description": "A kártya-elrendezés döntési szöge"
},
"card-animation": {
"label": "Kártya animáció",
"description": "Kártyaátmenetek sebessége"
},
"background-color": {
"label": "Háttérszín",
"description": "A sötétített fedőréteg színe"
},
"background-opacity": {
"label": "Háttér átlátszatlansága",
"description": "A sötétített fedőréteg átlátszatlansága"
},
"window-animation-speed": {
"label": "Ablak animáció sebessége",
"description": "A nyitás és zárás átmenetek sebessége"
},
"animate-window": {
"label": "Ablak animáció",
"description": "Nyitás és zárás átmenetek animálása"
},
"top-bar-height": {
"label": "Felső sáv magassága",
"description": "Az eszköztár magassága"
}
},
"shortcuts": {
"key-separator": "/",
"header": {
"navigation": "NAVIGÁCIÓ",
"actions": "MŰVELETEK",
"filters": "SZŰRŐK",
"view": "NÉZET",
"layout": "ELRENDEZÉS"
},
"label": {
"help-title": "Gyorsbillentyűk",
"navigate": "Navigálás",
"jump": "Ugrás",
"shuffle": "Keverés",
"apply-quit": "Alkalmazás + Kilépés",
"apply": "Alkalmazás",
"quit": "Kilépés",
"filter-all": "Összes",
"filter-images": "Képek",
"filter-videos": "Videók",
"filter-colors": "Színszűrő",
"top-bar": "Felső sáv",
"live-preview": "Élő előnézet",
"center-height": "Középső magasság",
"center-width": "Középső szélesség",
"cards-shown": "Megjelenített kártyák",
"cards-spacing": "Térköz",
"cards-width": "Kártyák szélessége",
"save": "Beállítások mentése",
"hide": "Elrejtés"
}
}
}
+116
View File
@@ -0,0 +1,116 @@
{
"buttons": {
"all": "Tutti",
"images": "Immagini",
"videos": "Video",
"shuffle": "Mescola",
"live-preview": "Live",
"color-na": "✕"
},
"widget": {
"tooltip": "Apri Wallcards",
"generate-thumbs-message": "Generazione miniature…"
},
"settings": {
"icon-color": {
"label": "Colore icona",
"description": "Colore dell'icona del widget della barra"
},
"live-preview": {
"label": "Anteprima dal vivo",
"description": "Applica lo sfondo durante la navigazione delle schede"
},
"default-filter": {
"label": "Filtro predefinito",
"description": "Filtro tipo file iniziale all'apertura"
},
"hide-shortcuts": {
"label": "Nascondi scorciatoie",
"description": "Nascondi il pannello delle scorciatoie da tastiera"
},
"hide-top-bar": {
"label": "Nascondi barra superiore",
"description": "Nascondi la barra degli strumenti sopra le schede"
},
"center-card-width": {
"label": "Larghezza scheda centrale",
"description": "Larghezza della scheda centrale in percentuale dello schermo"
},
"card-height": {
"label": "Altezza scheda",
"description": "Altezza delle schede sfondo"
},
"strip-width": {
"label": "Larghezza striscia",
"description": "Larghezza delle strisce laterali"
},
"card-spacing": {
"label": "Spaziatura schede",
"description": "Distanza tra le schede"
},
"cards-shown": {
"label": "Schede visibili",
"description": "Numero di schede visibili (numeri dispari consigliati)"
},
"shear-factor": {
"label": "Fattore di taglio",
"description": "Angolo di inclinazione del layout delle schede"
},
"card-animation": {
"label": "Animazione schede",
"description": "Velocità delle transizioni delle schede"
},
"background-color": {
"label": "Colore di sfondo",
"description": "Colore della sovrapposizione oscurata"
},
"background-opacity": {
"label": "Opacità dello sfondo",
"description": "Opacità della sovrapposizione oscurata"
},
"window-animation-speed": {
"label": "Velocità animazione finestra",
"description": "Velocità delle transizioni di apertura e chiusura"
},
"animate-window": {
"label": "Animazione finestra",
"description": "Anima le transizioni di apertura e chiusura"
},
"top-bar-height": {
"label": "Altezza barra superiore",
"description": "Altezza della barra degli strumenti"
}
},
"shortcuts": {
"key-separator": "/",
"header": {
"navigation": "NAVIGAZIONE",
"actions": "AZIONI",
"filters": "FILTRI",
"view": "VISTA",
"layout": "LAYOUT"
},
"label": {
"help-title": "Scorciatoie",
"navigate": "Naviga",
"jump": "Salta",
"shuffle": "Mescola",
"apply-quit": "Applica + Esci",
"apply": "Applica",
"quit": "Esci",
"filter-all": "Tutti",
"filter-images": "Immagini",
"filter-videos": "Video",
"filter-colors": "Filtro colore",
"top-bar": "Barra superiore",
"live-preview": "Anteprima dal vivo",
"center-height": "Altezza centrale",
"center-width": "Larghezza centrale",
"cards-shown": "Schede visibili",
"cards-spacing": "Spaziatura",
"cards-width": "Larghezza schede",
"save": "Salva impostazioni",
"hide": "Nascondi"
}
}
}
+116
View File
@@ -0,0 +1,116 @@
{
"buttons": {
"all": "すべて",
"images": "画像",
"videos": "動画",
"shuffle": "シャッフル",
"live-preview": "ライブ",
"color-na": "✕"
},
"widget": {
"tooltip": "Wallcardsを開く",
"generate-thumbs-message": "サムネイルを生成中…"
},
"settings": {
"icon-color": {
"label": "アイコンの色",
"description": "バーウィジェットのアイコンの色"
},
"live-preview": {
"label": "ライブプレビュー",
"description": "カードを閲覧しながら壁紙を適用"
},
"default-filter": {
"label": "デフォルトフィルター",
"description": "起動時のファイルタイプフィルター"
},
"hide-shortcuts": {
"label": "ショートカットを非表示",
"description": "キーボードショートカットパネルを非表示"
},
"hide-top-bar": {
"label": "トップバーを非表示",
"description": "カード上部のツールバーを非表示"
},
"center-card-width": {
"label": "中央カードの幅",
"description": "画面に対する中央カードの幅の割合"
},
"card-height": {
"label": "カードの高さ",
"description": "壁紙カードの高さ"
},
"strip-width": {
"label": "ストリップ幅",
"description": "サイドカードストリップの幅"
},
"card-spacing": {
"label": "カード間隔",
"description": "カード間のスペース"
},
"cards-shown": {
"label": "表示カード数",
"description": "表示するカードの数(奇数推奨)"
},
"shear-factor": {
"label": "せん断係数",
"description": "カードレイアウトの傾斜角度"
},
"card-animation": {
"label": "カードアニメーション",
"description": "カード遷移の速度"
},
"background-color": {
"label": "背景色",
"description": "暗転オーバーレイの色"
},
"background-opacity": {
"label": "背景の不透明度",
"description": "暗転オーバーレイの不透明度"
},
"window-animation-speed": {
"label": "ウィンドウアニメーション速度",
"description": "開閉時の遷移速度"
},
"animate-window": {
"label": "ウィンドウアニメーション",
"description": "開閉時の遷移をアニメーション化"
},
"top-bar-height": {
"label": "トップバーの高さ",
"description": "ツールバーの高さ"
}
},
"shortcuts": {
"key-separator": "/",
"header": {
"navigation": "ナビゲーション",
"actions": "アクション",
"filters": "フィルター",
"view": "表示",
"layout": "レイアウト"
},
"label": {
"help-title": "ショートカット",
"navigate": "移動",
"jump": "ジャンプ",
"shuffle": "シャッフル",
"apply-quit": "適用して終了",
"apply": "適用",
"quit": "終了",
"filter-all": "すべて",
"filter-images": "画像",
"filter-videos": "動画",
"filter-colors": "カラーフィルター",
"top-bar": "トップバー切替",
"live-preview": "ライブプレビュー切替",
"center-height": "中央の高さ",
"center-width": "中央の幅",
"cards-shown": "表示カード数",
"cards-spacing": "間隔",
"cards-width": "カード幅",
"save": "設定を保存",
"hide": "非表示"
}
}
}
+116
View File
@@ -0,0 +1,116 @@
{
"buttons": {
"all": "Hemû",
"images": "Wêne",
"videos": "Vîdeo",
"shuffle": "Tevlihev",
"live-preview": "Zindî",
"color-na": "✕"
},
"widget": {
"tooltip": "Wallcards veke",
"generate-thumbs-message": "Wêneyên biçûk têne çêkirin…"
},
"settings": {
"icon-color": {
"label": "Rengê îkonê",
"description": "Rengê îkona wîceta barê"
},
"live-preview": {
"label": "Pêşdîtina zindî",
"description": "Dîwarê bi kar bîne dema ku kartên digerin"
},
"default-filter": {
"label": "Parzûna standard",
"description": "Parzûna celebê pelê ya destpêkê dema vekirinê"
},
"hide-shortcuts": {
"label": "Kurterêyan veşêre",
"description": "Panela kurterêyên klavyeyê veşêre"
},
"hide-top-bar": {
"label": "Barê jorîn veşêre",
"description": "Amûrbarê li ser kartan veşêre"
},
"center-card-width": {
"label": "Firehiya karta navendî",
"description": "Firehiya karta navendî wek sedî ya ekranê"
},
"card-height": {
"label": "Bilindahiya kartê",
"description": "Bilindahiya kartên dîwarê"
},
"strip-width": {
"label": "Firehiya şirîtê",
"description": "Firehiya şirîtên kartên alî"
},
"card-spacing": {
"label": "Navbera kartan",
"description": "Valahiya di navbera kartan de"
},
"cards-shown": {
"label": "Kartên xuya",
"description": "Hejmara kartên xuya (jimarên taq têne pêşniyar kirin)"
},
"shear-factor": {
"label": "Faktora qutkirinê",
"description": "Goşeya meylkirina sêwirana kartan"
},
"card-animation": {
"label": "Anîmasyona kartê",
"description": "Leza veguheztinên kartan"
},
"background-color": {
"label": "Rengê paşperdê",
"description": "Rengê tebeqa tarîkirî"
},
"background-opacity": {
"label": "Zelalbûna paşperdê",
"description": "Zelalbûna tebeqa tarîkirî"
},
"window-animation-speed": {
"label": "Leza anîmasyona pencereyê",
"description": "Leza veguheztinên vekirî û girtî"
},
"animate-window": {
"label": "Anîmasyona pencereyê",
"description": "Veguheztinên vekirî û girtî bianîme"
},
"top-bar-height": {
"label": "Bilindahiya barê jorîn",
"description": "Bilindahiya amûrbarê"
}
},
"shortcuts": {
"key-separator": "/",
"header": {
"navigation": "NAVÎGASYON",
"actions": "ÇALAKÎ",
"filters": "PARZÛN",
"view": "DÎTIN",
"layout": "SÊWIRAN"
},
"label": {
"help-title": "Kurterê",
"navigate": "Navîgasyon",
"jump": "Bazdan",
"shuffle": "Tevlihev",
"apply-quit": "Bicîh bîne + Derkeve",
"apply": "Bicîh bîne",
"quit": "Derkeve",
"filter-all": "Hemû",
"filter-images": "Wêne",
"filter-videos": "Vîdeo",
"filter-colors": "Parzûna rengan",
"top-bar": "Barê jorîn",
"live-preview": "Pêşdîtina zindî",
"center-height": "Bilindahiya navendî",
"center-width": "Firehiya navendî",
"cards-shown": "Kartên xuya",
"cards-spacing": "Navber",
"cards-width": "Firehiya kartan",
"save": "Mîhengan tomar bike",
"hide": "Veşêre"
}
}
}
+116
View File
@@ -0,0 +1,116 @@
{
"buttons": {
"all": "Alles",
"images": "Afbeeldingen",
"videos": "Video's",
"shuffle": "Willekeurig",
"live-preview": "Live",
"color-na": "✕"
},
"widget": {
"tooltip": "Wallcards openen",
"generate-thumbs-message": "Miniaturen genereren…"
},
"settings": {
"icon-color": {
"label": "Pictogramkleur",
"description": "Kleur van het balkwidget-pictogram"
},
"live-preview": {
"label": "Live voorbeeld",
"description": "Achtergrond toepassen tijdens het bladeren"
},
"default-filter": {
"label": "Standaardfilter",
"description": "Initieel bestandstypefilter bij openen"
},
"hide-shortcuts": {
"label": "Sneltoetsen verbergen",
"description": "Het sneltoetsenpaneel verbergen"
},
"hide-top-bar": {
"label": "Bovenste balk verbergen",
"description": "De werkbalk boven de kaarten verbergen"
},
"center-card-width": {
"label": "Breedte middelste kaart",
"description": "Breedte van de middelste kaart als percentage van het scherm"
},
"card-height": {
"label": "Kaarthoogte",
"description": "Hoogte van de achtergrondkaarten"
},
"strip-width": {
"label": "Stripbreedte",
"description": "Breedte van de zijstroken"
},
"card-spacing": {
"label": "Kaartafstand",
"description": "Ruimte tussen kaarten"
},
"cards-shown": {
"label": "Zichtbare kaarten",
"description": "Aantal zichtbare kaarten (oneven aantallen aanbevolen)"
},
"shear-factor": {
"label": "Schuinfactor",
"description": "Hellingshoek van de kaartindeling"
},
"card-animation": {
"label": "Kaartanimatie",
"description": "Snelheid van kaartovergangen"
},
"background-color": {
"label": "Achtergrondkleur",
"description": "Kleur van de verduisterde overlay"
},
"background-opacity": {
"label": "Achtergrond-dekking",
"description": "Dekking van de verduisterde overlay"
},
"window-animation-speed": {
"label": "Vensteranimatiesnelheid",
"description": "Snelheid van open- en sluitovergangen"
},
"animate-window": {
"label": "Vensteranimatie",
"description": "Open- en sluitovergangen animeren"
},
"top-bar-height": {
"label": "Hoogte bovenste balk",
"description": "Hoogte van de werkbalk"
}
},
"shortcuts": {
"key-separator": "/",
"header": {
"navigation": "NAVIGATIE",
"actions": "ACTIES",
"filters": "FILTERS",
"view": "WEERGAVE",
"layout": "INDELING"
},
"label": {
"help-title": "Sneltoetsen",
"navigate": "Navigeren",
"jump": "Springen",
"shuffle": "Willekeurig",
"apply-quit": "Toepassen + Sluiten",
"apply": "Toepassen",
"quit": "Sluiten",
"filter-all": "Alles",
"filter-images": "Afbeeldingen",
"filter-videos": "Video's",
"filter-colors": "Kleurfilter",
"top-bar": "Bovenste balk",
"live-preview": "Live voorbeeld",
"center-height": "Middenhoogte",
"center-width": "Middenbreedte",
"cards-shown": "Zichtbare kaarten",
"cards-spacing": "Afstand",
"cards-width": "Kaartbreedte",
"save": "Instellingen opslaan",
"hide": "Verbergen"
}
}
}
+116
View File
@@ -0,0 +1,116 @@
{
"buttons": {
"all": "Wszystkie",
"images": "Obrazy",
"videos": "Filmy",
"shuffle": "Losuj",
"live-preview": "Na żywo",
"color-na": "✕"
},
"widget": {
"tooltip": "Otwórz Wallcards",
"generate-thumbs-message": "Generowanie miniatur…"
},
"settings": {
"icon-color": {
"label": "Kolor ikony",
"description": "Kolor ikony widżetu paska"
},
"live-preview": {
"label": "Podgląd na żywo",
"description": "Zastosuj tapetę podczas przeglądania kart"
},
"default-filter": {
"label": "Domyślny filtr",
"description": "Początkowy filtr typu pliku przy otwieraniu"
},
"hide-shortcuts": {
"label": "Ukryj skróty",
"description": "Ukryj panel skrótów klawiszowych"
},
"hide-top-bar": {
"label": "Ukryj górny pasek",
"description": "Ukryj pasek narzędzi nad kartami"
},
"center-card-width": {
"label": "Szerokość środkowej karty",
"description": "Szerokość środkowej karty jako procent ekranu"
},
"card-height": {
"label": "Wysokość karty",
"description": "Wysokość kart tapet"
},
"strip-width": {
"label": "Szerokość paska",
"description": "Szerokość bocznych pasków kart"
},
"card-spacing": {
"label": "Odstęp kart",
"description": "Przerwa między kartami"
},
"cards-shown": {
"label": "Widoczne karty",
"description": "Liczba widocznych kart (zalecane liczby nieparzyste)"
},
"shear-factor": {
"label": "Współczynnik ścinania",
"description": "Kąt pochylenia układu kart"
},
"card-animation": {
"label": "Animacja kart",
"description": "Szybkość przejść kart"
},
"background-color": {
"label": "Kolor tła",
"description": "Kolor przyciemnionej nakładki"
},
"background-opacity": {
"label": "Nieprzezroczystość tła",
"description": "Nieprzezroczystość przyciemnionej nakładki"
},
"window-animation-speed": {
"label": "Szybkość animacji okna",
"description": "Szybkość przejść otwierania i zamykania"
},
"animate-window": {
"label": "Animacja okna",
"description": "Animuj przejścia otwierania i zamykania"
},
"top-bar-height": {
"label": "Wysokość górnego paska",
"description": "Wysokość paska narzędzi"
}
},
"shortcuts": {
"key-separator": "/",
"header": {
"navigation": "NAWIGACJA",
"actions": "AKCJE",
"filters": "FILTRY",
"view": "WIDOK",
"layout": "UKŁAD"
},
"label": {
"help-title": "Skróty",
"navigate": "Nawiguj",
"jump": "Skocz",
"shuffle": "Losuj",
"apply-quit": "Zastosuj + Zamknij",
"apply": "Zastosuj",
"quit": "Zamknij",
"filter-all": "Wszystkie",
"filter-images": "Obrazy",
"filter-videos": "Filmy",
"filter-colors": "Filtr kolorów",
"top-bar": "Górny pasek",
"live-preview": "Podgląd na żywo",
"center-height": "Wysokość środka",
"center-width": "Szerokość środka",
"cards-shown": "Widoczne karty",
"cards-spacing": "Odstęp",
"cards-width": "Szerokość kart",
"save": "Zapisz ustawienia",
"hide": "Ukryj"
}
}
}
+116
View File
@@ -0,0 +1,116 @@
{
"buttons": {
"all": "Todos",
"images": "Imagens",
"videos": "Vídeos",
"shuffle": "Aleatório",
"live-preview": "Ao vivo",
"color-na": "✕"
},
"widget": {
"tooltip": "Abrir Wallcards",
"generate-thumbs-message": "Gerando miniaturas…"
},
"settings": {
"icon-color": {
"label": "Cor do ícone",
"description": "Cor do ícone do widget da barra"
},
"live-preview": {
"label": "Pré-visualização ao vivo",
"description": "Aplicar papel de parede enquanto navega pelos cartões"
},
"default-filter": {
"label": "Filtro padrão",
"description": "Filtro de tipo de arquivo inicial ao abrir"
},
"hide-shortcuts": {
"label": "Ocultar atalhos",
"description": "Ocultar o painel de atalhos de teclado"
},
"hide-top-bar": {
"label": "Ocultar barra superior",
"description": "Ocultar a barra de ferramentas acima dos cartões"
},
"center-card-width": {
"label": "Largura do cartão central",
"description": "Largura do cartão central como porcentagem da tela"
},
"card-height": {
"label": "Altura do cartão",
"description": "Altura dos cartões de papel de parede"
},
"strip-width": {
"label": "Largura da faixa",
"description": "Largura das faixas laterais dos cartões"
},
"card-spacing": {
"label": "Espaçamento dos cartões",
"description": "Espaço entre os cartões"
},
"cards-shown": {
"label": "Cartões visíveis",
"description": "Número de cartões visíveis (números ímpares recomendados)"
},
"shear-factor": {
"label": "Fator de cisalhamento",
"description": "Ângulo de inclinação do layout dos cartões"
},
"card-animation": {
"label": "Animação dos cartões",
"description": "Velocidade das transições dos cartões"
},
"background-color": {
"label": "Cor de fundo",
"description": "Cor da sobreposição escurecida"
},
"background-opacity": {
"label": "Opacidade do fundo",
"description": "Opacidade da sobreposição escurecida"
},
"window-animation-speed": {
"label": "Velocidade da animação da janela",
"description": "Velocidade das transições de abrir e fechar"
},
"animate-window": {
"label": "Animação da janela",
"description": "Animar as transições de abrir e fechar"
},
"top-bar-height": {
"label": "Altura da barra superior",
"description": "Altura da barra de ferramentas"
}
},
"shortcuts": {
"key-separator": "/",
"header": {
"navigation": "NAVEGAÇÃO",
"actions": "AÇÕES",
"filters": "FILTROS",
"view": "VISUALIZAÇÃO",
"layout": "LAYOUT"
},
"label": {
"help-title": "Atalhos",
"navigate": "Navegar",
"jump": "Saltar",
"shuffle": "Aleatório",
"apply-quit": "Aplicar + Fechar",
"apply": "Aplicar",
"quit": "Fechar",
"filter-all": "Todos",
"filter-images": "Imagens",
"filter-videos": "Vídeos",
"filter-colors": "Filtro de cor",
"top-bar": "Barra superior",
"live-preview": "Pré-visualização",
"center-height": "Altura central",
"center-width": "Largura central",
"cards-shown": "Cartões visíveis",
"cards-spacing": "Espaçamento",
"cards-width": "Largura dos cartões",
"save": "Salvar configurações",
"hide": "Ocultar"
}
}
}
+116
View File
@@ -0,0 +1,116 @@
{
"buttons": {
"all": "Все",
"images": "Изображения",
"videos": "Видео",
"shuffle": "Перемешать",
"live-preview": "Живой",
"color-na": "✕"
},
"widget": {
"tooltip": "Открыть Wallcards",
"generate-thumbs-message": "Создание миниатюр…"
},
"settings": {
"icon-color": {
"label": "Цвет значка",
"description": "Цвет значка виджета панели"
},
"live-preview": {
"label": "Предпросмотр",
"description": "Применять обои при просмотре карточек"
},
"default-filter": {
"label": "Фильтр по умолчанию",
"description": "Начальный фильтр типа файлов при открытии"
},
"hide-shortcuts": {
"label": "Скрыть горячие клавиши",
"description": "Скрыть панель горячих клавиш"
},
"hide-top-bar": {
"label": "Скрыть верхнюю панель",
"description": "Скрыть панель инструментов над карточками"
},
"center-card-width": {
"label": "Ширина центральной карточки",
"description": "Ширина центральной карточки в процентах от экрана"
},
"card-height": {
"label": "Высота карточки",
"description": "Высота карточек обоев"
},
"strip-width": {
"label": "Ширина полосы",
"description": "Ширина боковых полос карточек"
},
"card-spacing": {
"label": "Расстояние между карточками",
"description": "Зазор между карточками"
},
"cards-shown": {
"label": "Видимые карточки",
"description": "Количество видимых карточек (рекомендуются нечётные числа)"
},
"shear-factor": {
"label": "Коэффициент сдвига",
"description": "Угол наклона расположения карточек"
},
"card-animation": {
"label": "Анимация карточек",
"description": "Скорость переходов карточек"
},
"background-color": {
"label": "Цвет фона",
"description": "Цвет затемнённого наложения"
},
"background-opacity": {
"label": "Прозрачность фона",
"description": "Прозрачность затемнённого наложения"
},
"window-animation-speed": {
"label": "Скорость анимации окна",
"description": "Скорость переходов открытия и закрытия"
},
"animate-window": {
"label": "Анимация окна",
"description": "Анимировать переходы открытия и закрытия"
},
"top-bar-height": {
"label": "Высота верхней панели",
"description": "Высота панели инструментов"
}
},
"shortcuts": {
"key-separator": "/",
"header": {
"navigation": "НАВИГАЦИЯ",
"actions": "ДЕЙСТВИЯ",
"filters": "ФИЛЬТРЫ",
"view": "ВИД",
"layout": "КОМПОНОВКА"
},
"label": {
"help-title": "Горячие клавиши",
"navigate": "Навигация",
"jump": "Перейти",
"shuffle": "Перемешать",
"apply-quit": "Применить + Выйти",
"apply": "Применить",
"quit": "Выйти",
"filter-all": "Все",
"filter-images": "Изображения",
"filter-videos": "Видео",
"filter-colors": "Цветовой фильтр",
"top-bar": "Верхняя панель",
"live-preview": "Предпросмотр",
"center-height": "Высота центра",
"center-width": "Ширина центра",
"cards-shown": "Видимые карточки",
"cards-spacing": "Расстояние",
"cards-width": "Ширина карточек",
"save": "Сохранить настройки",
"hide": "Скрыть"
}
}
}
+116
View File
@@ -0,0 +1,116 @@
{
"buttons": {
"all": "Tümü",
"images": "Resimler",
"videos": "Videolar",
"shuffle": "Karıştır",
"live-preview": "Canlı",
"color-na": "✕"
},
"widget": {
"tooltip": "Wallcards'ı aç",
"generate-thumbs-message": "Küçük resimler oluşturuluyor…"
},
"settings": {
"icon-color": {
"label": "Simge rengi",
"description": "Çubuk widget simgesinin rengi"
},
"live-preview": {
"label": "Canlı önizleme",
"description": "Kartlara göz atarken duvar kağıdını uygula"
},
"default-filter": {
"label": "Varsayılan filtre",
"description": "Açılışta başlangıç dosya türü filtresi"
},
"hide-shortcuts": {
"label": "Kısayolları gizle",
"description": "Klavye kısayolları panelini gizle"
},
"hide-top-bar": {
"label": "Üst çubuğu gizle",
"description": "Kartların üzerindeki araç çubuğunu gizle"
},
"center-card-width": {
"label": "Orta kart genişliği",
"description": "Orta kartın ekran yüzdesi olarak genişliği"
},
"card-height": {
"label": "Kart yüksekliği",
"description": "Duvar kağıdı kartlarının yüksekliği"
},
"strip-width": {
"label": "Şerit genişliği",
"description": "Yan kart şeritlerinin genişliği"
},
"card-spacing": {
"label": "Kart aralığı",
"description": "Kartlar arasındaki boşluk"
},
"cards-shown": {
"label": "Görünen kartlar",
"description": "Görünür kart sayısı (tek sayılar önerilir)"
},
"shear-factor": {
"label": "Kayma faktörü",
"description": "Kart düzeninin eğim açısı"
},
"card-animation": {
"label": "Kart animasyonu",
"description": "Kart geçişlerinin hızı"
},
"background-color": {
"label": "Arka plan rengi",
"description": "Karartılmış katmanın rengi"
},
"background-opacity": {
"label": "Arka plan opaklığı",
"description": "Karartılmış katmanın opaklığı"
},
"window-animation-speed": {
"label": "Pencere animasyon hızı",
"description": "Açma ve kapama geçişlerinin hızı"
},
"animate-window": {
"label": "Pencere animasyonu",
"description": "Açma ve kapama geçişlerini animasyonla göster"
},
"top-bar-height": {
"label": "Üst çubuk yüksekliği",
"description": "Araç çubuğunun yüksekliği"
}
},
"shortcuts": {
"key-separator": "/",
"header": {
"navigation": "GEZİNME",
"actions": "EYLEMLER",
"filters": "FİLTRELER",
"view": "GÖRÜNÜM",
"layout": "DÜZEN"
},
"label": {
"help-title": "Kısayollar",
"navigate": "Gezin",
"jump": "Atla",
"shuffle": "Karıştır",
"apply-quit": "Uygula + Kapat",
"apply": "Uygula",
"quit": "Kapat",
"filter-all": "Tümü",
"filter-images": "Resimler",
"filter-videos": "Videolar",
"filter-colors": "Renk filtresi",
"top-bar": "Üst çubuk",
"live-preview": "Canlı önizleme",
"center-height": "Orta yükseklik",
"center-width": "Orta genişlik",
"cards-shown": "Görünen kartlar",
"cards-spacing": "Aralık",
"cards-width": "Kart genişliği",
"save": "Ayarları kaydet",
"hide": "Gizle"
}
}
}
+116
View File
@@ -0,0 +1,116 @@
{
"buttons": {
"all": "Усі",
"images": "Зображення",
"videos": "Відео",
"shuffle": "Перемішати",
"live-preview": "Наживо",
"color-na": "✕"
},
"widget": {
"tooltip": "Відкрити Wallcards",
"generate-thumbs-message": "Створення мініатюр…"
},
"settings": {
"icon-color": {
"label": "Колір значка",
"description": "Колір значка віджета панелі"
},
"live-preview": {
"label": "Попередній перегляд",
"description": "Застосовувати шпалери під час перегляду карток"
},
"default-filter": {
"label": "Фільтр за замовчуванням",
"description": "Початковий фільтр типу файлів при відкритті"
},
"hide-shortcuts": {
"label": "Сховати гарячі клавіші",
"description": "Сховати панель гарячих клавіш"
},
"hide-top-bar": {
"label": "Сховати верхню панель",
"description": "Сховати панель інструментів над картками"
},
"center-card-width": {
"label": "Ширина центральної картки",
"description": "Ширина центральної картки у відсотках екрана"
},
"card-height": {
"label": "Висота картки",
"description": "Висота карток шпалер"
},
"strip-width": {
"label": "Ширина смуги",
"description": "Ширина бічних смуг карток"
},
"card-spacing": {
"label": "Відстань між картками",
"description": "Проміжок між картками"
},
"cards-shown": {
"label": "Видимі картки",
"description": "Кількість видимих карток (рекомендуються непарні числа)"
},
"shear-factor": {
"label": "Коефіцієнт зсуву",
"description": "Кут нахилу розташування карток"
},
"card-animation": {
"label": "Анімація карток",
"description": "Швидкість переходів карток"
},
"background-color": {
"label": "Колір фону",
"description": "Колір затемненого накладання"
},
"background-opacity": {
"label": "Прозорість фону",
"description": "Прозорість затемненого накладання"
},
"window-animation-speed": {
"label": "Швидкість анімації вікна",
"description": "Швидкість переходів відкриття та закриття"
},
"animate-window": {
"label": "Анімація вікна",
"description": "Анімувати переходи відкриття та закриття"
},
"top-bar-height": {
"label": "Висота верхньої панелі",
"description": "Висота панелі інструментів"
}
},
"shortcuts": {
"key-separator": "/",
"header": {
"navigation": "НАВІГАЦІЯ",
"actions": "ДІЇ",
"filters": "ФІЛЬТРИ",
"view": "ВИГЛЯД",
"layout": "КОМПОНУВАННЯ"
},
"label": {
"help-title": "Гарячі клавіші",
"navigate": "Навігація",
"jump": "Перейти",
"shuffle": "Перемішати",
"apply-quit": "Застосувати + Вийти",
"apply": "Застосувати",
"quit": "Вийти",
"filter-all": "Усі",
"filter-images": "Зображення",
"filter-videos": "Відео",
"filter-colors": "Кольоровий фільтр",
"top-bar": "Верхня панель",
"live-preview": "Попередній перегляд",
"center-height": "Висота центру",
"center-width": "Ширина центру",
"cards-shown": "Видимі картки",
"cards-spacing": "Відстань",
"cards-width": "Ширина карток",
"save": "Зберегти налаштування",
"hide": "Сховати"
}
}
}
+116
View File
@@ -0,0 +1,116 @@
{
"buttons": {
"all": "Tất cả",
"images": "Hình ảnh",
"videos": "Video",
"shuffle": "Xáo trộn",
"live-preview": "Trực tiếp",
"color-na": "✕"
},
"widget": {
"tooltip": "Mở Wallcards",
"generate-thumbs-message": "Đang tạo hình thu nhỏ…"
},
"settings": {
"icon-color": {
"label": "Màu biểu tượng",
"description": "Màu của biểu tượng widget thanh"
},
"live-preview": {
"label": "Xem trước trực tiếp",
"description": "Áp dụng hình nền khi duyệt thẻ"
},
"default-filter": {
"label": "Bộ lọc mặc định",
"description": "Bộ lọc loại tệp ban đầu khi mở"
},
"hide-shortcuts": {
"label": "Ẩn phím tắt",
"description": "Ẩn bảng phím tắt bàn phím"
},
"hide-top-bar": {
"label": "Ẩn thanh trên",
"description": "Ẩn thanh công cụ phía trên các thẻ"
},
"center-card-width": {
"label": "Chiều rộng thẻ giữa",
"description": "Chiều rộng thẻ giữa tính theo phần trăm màn hình"
},
"card-height": {
"label": "Chiều cao thẻ",
"description": "Chiều cao của các thẻ hình nền"
},
"strip-width": {
"label": "Chiều rộng dải",
"description": "Chiều rộng các dải thẻ bên"
},
"card-spacing": {
"label": "Khoảng cách thẻ",
"description": "Khoảng cách giữa các thẻ"
},
"cards-shown": {
"label": "Số thẻ hiển thị",
"description": "Số lượng thẻ hiển thị (khuyến nghị số lẻ)"
},
"shear-factor": {
"label": "Hệ số nghiêng",
"description": "Góc nghiêng của bố cục thẻ"
},
"card-animation": {
"label": "Hoạt ảnh thẻ",
"description": "Tốc độ chuyển đổi thẻ"
},
"background-color": {
"label": "Màu nền",
"description": "Màu của lớp phủ tối"
},
"background-opacity": {
"label": "Độ mờ nền",
"description": "Độ mờ của lớp phủ tối"
},
"window-animation-speed": {
"label": "Tốc độ hoạt ảnh cửa sổ",
"description": "Tốc độ chuyển đổi mở và đóng"
},
"animate-window": {
"label": "Hoạt ảnh cửa sổ",
"description": "Tạo hoạt ảnh cho chuyển đổi mở và đóng"
},
"top-bar-height": {
"label": "Chiều cao thanh trên",
"description": "Chiều cao của thanh công cụ"
}
},
"shortcuts": {
"key-separator": "/",
"header": {
"navigation": "ĐIỀU HƯỚNG",
"actions": "HÀNH ĐỘNG",
"filters": "BỘ LỌC",
"view": "HIỂN THỊ",
"layout": "BỐ CỤC"
},
"label": {
"help-title": "Phím tắt",
"navigate": "Di chuyển",
"jump": "Nhảy",
"shuffle": "Xáo trộn",
"apply-quit": "Áp dụng + Thoát",
"apply": "Áp dụng",
"quit": "Thoát",
"filter-all": "Tất cả",
"filter-images": "Hình ảnh",
"filter-videos": "Video",
"filter-colors": "Lọc màu",
"top-bar": "Thanh trên",
"live-preview": "Xem trước trực tiếp",
"center-height": "Chiều cao giữa",
"center-width": "Chiều rộng giữa",
"cards-shown": "Số thẻ hiển thị",
"cards-spacing": "Khoảng cách",
"cards-width": "Chiều rộng thẻ",
"save": "Lưu cài đặt",
"hide": "Ẩn"
}
}
}
+116
View File
@@ -0,0 +1,116 @@
{
"buttons": {
"all": "全部",
"images": "图片",
"videos": "视频",
"shuffle": "随机",
"live-preview": "实时",
"color-na": "✕"
},
"widget": {
"tooltip": "打开 Wallcards",
"generate-thumbs-message": "正在生成缩略图…"
},
"settings": {
"icon-color": {
"label": "图标颜色",
"description": "栏小部件图标的颜色"
},
"live-preview": {
"label": "实时预览",
"description": "浏览卡片时应用壁纸"
},
"default-filter": {
"label": "默认筛选",
"description": "打开时的初始文件类型筛选"
},
"hide-shortcuts": {
"label": "隐藏快捷键",
"description": "隐藏键盘快捷键面板"
},
"hide-top-bar": {
"label": "隐藏顶栏",
"description": "隐藏卡片上方的工具栏"
},
"center-card-width": {
"label": "中央卡片宽度",
"description": "中央卡片占屏幕的宽度百分比"
},
"card-height": {
"label": "卡片高度",
"description": "壁纸卡片的高度"
},
"strip-width": {
"label": "条带宽度",
"description": "侧面卡片条带的宽度"
},
"card-spacing": {
"label": "卡片间距",
"description": "卡片之间的间隔"
},
"cards-shown": {
"label": "显示卡片数",
"description": "可见卡片数量(建议使用奇数)"
},
"shear-factor": {
"label": "剪切系数",
"description": "卡片布局的倾斜角度"
},
"card-animation": {
"label": "卡片动画",
"description": "卡片过渡的速度"
},
"background-color": {
"label": "背景颜色",
"description": "暗化覆盖层的颜色"
},
"background-opacity": {
"label": "背景不透明度",
"description": "暗化覆盖层的不透明度"
},
"window-animation-speed": {
"label": "窗口动画速度",
"description": "打开和关闭过渡的速度"
},
"animate-window": {
"label": "窗口动画",
"description": "为打开和关闭过渡添加动画"
},
"top-bar-height": {
"label": "顶栏高度",
"description": "工具栏的高度"
}
},
"shortcuts": {
"key-separator": "/",
"header": {
"navigation": "导航",
"actions": "操作",
"filters": "筛选",
"view": "视图",
"layout": "布局"
},
"label": {
"help-title": "快捷键",
"navigate": "导航",
"jump": "跳转",
"shuffle": "随机",
"apply-quit": "应用并退出",
"apply": "应用",
"quit": "退出",
"filter-all": "全部",
"filter-images": "图片",
"filter-videos": "视频",
"filter-colors": "颜色筛选",
"top-bar": "顶栏",
"live-preview": "实时预览",
"center-height": "中央高度",
"center-width": "中央宽度",
"cards-shown": "显示卡片数",
"cards-spacing": "间距",
"cards-width": "卡片宽度",
"save": "保存设置",
"hide": "隐藏"
}
}
}
+41
View File
@@ -0,0 +1,41 @@
{
"id": "wallcards",
"name": "Wallcards",
"version": "0.1.1",
"minNoctaliaVersion": "4.7.0",
"author": "tonigineer",
"license": "MIT",
"repository": "https://github.com/noctalia-dev/noctalia-plugins",
"description": "A `lively` wallpaper selector.",
"tags": ["Desktop", "Theming"],
"entryPoints": {
"main": "Main.qml",
"barWidget": "BarWidget.qml",
"settings": "Settings.qml"
},
"dependencies": {
"plugins": []
},
"metadata": {
"defaultSettings": {
"animation_cards_duration": 750,
"animation_window_duration": 500,
"animate_window": true,
"background_color": null,
"background_opacity": null,
"card_height": 350,
"card_spacing": 6,
"card_strip_width": 90,
"cards_shown": 11,
"center_width_ratio": 0.33,
"image_filter": ["png", "jpg", "jpeg"],
"video_filter": ["mp4", "avi"],
"hide_help": false,
"hide_top_bar": false,
"live_preview": false,
"selected_filter": "images",
"shear_factor": -0.25,
"top_bar_height": 35
}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 MiB

+153
View File
@@ -0,0 +1,153 @@
import qs.Commons
import qs.Widgets
import QtQuick
import QtMultimedia
import Qt5Compat.GraphicalEffects
Item {
id: cardContent
required property int animationDuration
required property real centerWidth
required property string filePath
required property bool isCenter
required property bool isVideo
required property real radius
required property string thumbnailPath
anchors.fill: parent
clip: true
Item {
id: imgComposite
height: cardContent.height
visible: false
width: cardContent.centerWidth
x: (cardContent.width - cardContent.centerWidth) / 2
Image {
id: img
anchors.fill: parent
// asynchronous: true // TODO: flickering when going backwards
cache: true
fillMode: Image.PreserveAspectCrop
smooth: true
source: cardContent.thumbnailPath ? "file://" + cardContent.thumbnailPath : ""
sourceSize.height: parent.height
sourceSize.width: cardContent.centerWidth
}
}
Rectangle {
id: border
anchors.fill: parent
border.color: isCenter ? Color.mOutline : Qt.alpha(Color.mOutlineVariant, 0.5)
border.width: Style.borderS
color: "transparent"
opacity: 0.75
radius: cardContent.radius
z: 20
}
Rectangle {
id: mask
anchors.fill: parent
radius: cardContent.radius
visible: false
}
OpacityMask {
anchors.fill: parent
maskSource: mask
source: ShaderEffectSource {
sourceItem: imgComposite
sourceRect: Qt.rect(-imgComposite.x, 0, cardContent.width, cardContent.height)
}
}
Loader {
id: videoLoader
property bool shouldLoad: false
property string videoPath: cardContent.isCenter && cardContent.isVideo ? cardContent.filePath : ""
active: shouldLoad && videoPath !== ""
anchors.fill: parent
z: 5
sourceComponent: Component {
Item {
id: videoContainer
anchors.fill: parent
layer.enabled: true
opacity: 0
layer.effect: OpacityMask {
maskSource: Rectangle {
height: videoContainer.height
radius: cardContent.radius
width: videoContainer.width
}
}
MediaPlayer {
id: mediaPlayer
loops: MediaPlayer.Infinite
source: "file://" + videoLoader.videoPath
videoOutput: videoOutput
audioOutput: AudioOutput {
volume: 0
}
Component.onCompleted: play()
onPlayingChanged: function () {
if (mediaPlayer.playing)
videoFadeIn.start();
}
}
VideoOutput {
id: videoOutput
anchors.fill: parent
fillMode: VideoOutput.PreserveAspectCrop
}
NumberAnimation {
id: videoFadeIn
duration: cardContent.animationDuration
easing.type: Easing.OutCubic
from: 0
property: "opacity"
target: videoContainer
to: 1
}
}
}
onVideoPathChanged: {
shouldLoad = false;
if (videoPath !== "")
videoDelayTimer.restart();
else
videoDelayTimer.stop();
}
Timer {
id: videoDelayTimer
interval: cardContent.animationDuration
onTriggered: videoLoader.shouldLoad = true
}
}
}
+162
View File
@@ -0,0 +1,162 @@
import qs.Commons
import qs.Widgets
import QtQuick
Item {
id: cardDeck
required property int animationDuration
property real animationIndex: 0
required property int cardRadius
required property int cardSpacing
required property int cardStripWidth
required property int cardsShown
property real centerWidth: parent.width * centerWidthRatio
required property real centerWidthRatio
property real centerX: width / 2 - centerWidth / 2
property int currentIndex: 0
required property int filteredCount
required property var filteredModel
property int halfVisible: Math.floor(visibleCount / 2)
property real runningIndex: 0
required property real shearFactor
property int sideCount: Math.floor(cardsShown / 2) - 1
property int visibleCount: cardsShown
signal applyRequested(int index)
function navigateTo(idx) {
var maxLead = Math.max(1, halfVisible - 1);
if (Math.abs(runningIndex - animationIndex) >= maxLead)
return;
var newIdx = wrappedIndex(idx);
var diff = 0;
if (filteredCount > 0) {
diff = newIdx - currentIndex;
var half = filteredCount / 2;
if (diff > half)
diff -= filteredCount;
else if (diff < -half)
diff += filteredCount;
}
runningIndex += diff;
animationIndex = runningIndex;
currentIndex = newIdx;
}
function randomJump() {
var rnd = Math.floor(Math.random() * filteredCount);
if (rnd === currentIndex)
rnd = (rnd + 1) % filteredCount;
navigateTo(rnd);
}
function slotToWidth(slot) {
var t = Math.min(Math.abs(slot), 1);
return centerWidth + (cardStripWidth - centerWidth) * t;
}
function slotToX(slot) {
var rightEdge = centerX + centerWidth + cardSpacing;
var leftEdge = centerX - cardSpacing - cardStripWidth;
if (slot >= 0 && slot <= 1)
return centerX + (rightEdge - centerX) * slot;
if (slot > 1)
return rightEdge + (slot - 1) * (cardStripWidth + cardSpacing);
if (slot >= -1 && slot < 0)
return centerX + (leftEdge - centerX) * (-slot);
if (slot < -1)
return leftEdge + (slot + 1) * (cardStripWidth + cardSpacing);
return 0;
}
function wrappedIndex(idx) {
return ((idx % filteredCount) + filteredCount) % filteredCount;
}
function jumpTo(idx) {
animBehavior.enabled = false;
runningIndex = idx;
animationIndex = idx;
currentIndex = idx;
reenableTimer.restart();
}
Timer {
id: reenableTimer
interval: 0
onTriggered: animBehavior.enabled = true
}
width: (cardsShown - 3) * cardStripWidth + (cardsShown - 3) * cardSpacing + centerWidth
Behavior on animationIndex {
id: animBehavior
NumberAnimation {
duration: cardDeck.animationDuration
easing.type: Easing.OutCubic
}
}
transform: Matrix4x4 {
property real s: cardDeck.shearFactor
matrix: Qt.matrix4x4(1, s, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)
}
Repeater {
model: cardDeck.filteredCount > 0 ? cardDeck.visibleCount : 0
delegate: Rectangle {
id: card
property real fractionalSlot: offset + (cardDeck.runningIndex - cardDeck.animationIndex)
property bool isCenter: offset === 0
property int modelIndex: cardDeck.wrappedIndex(Math.round(cardDeck.runningIndex) + offset)
property int offset: index - cardDeck.halfVisible
signal applyRequested
border.color: isCenter ? Color.mOutline : Color.mSurface
border.width: isCenter ? Style.borderM : Style.borderS
color: "transparent"
height: cardDeck.height
opacity: Math.max(0, Math.min(1, cardDeck.halfVisible - Math.abs(fractionalSlot)))
radius: cardDeck.cardRadius
visible: (x + width) > 0 && x < cardDeck.width
width: cardDeck.slotToWidth(fractionalSlot)
x: cardDeck.slotToX(fractionalSlot)
y: 0
z: isCenter ? 100 : cardDeck.visibleCount - Math.abs(offset)
Card {
animationDuration: cardDeck.animationDuration
centerWidth: cardDeck.centerWidth
filePath: cardDeck.filteredModel[card.modelIndex]?.filePath ?? ""
isCenter: card.isCenter
isVideo: cardDeck.filteredModel[card.modelIndex]?.isVideo ?? false
radius: cardDeck.cardRadius
thumbnailPath: cardDeck.filteredModel[card.modelIndex]?.thumbnail ?? ""
}
MouseArea {
anchors.fill: parent
cursorShape: card.isCenter ? Qt.PointingHandCursor : Qt.ArrowCursor
onClicked: {
if (card.isCenter)
cardDeck.applyRequested(card.modelIndex);
}
//
onWheel: function (wheel) {
if (wheel.angleDelta.y > 0)
cardDeck.navigateTo(cardDeck.currentIndex - 1);
else if (wheel.angleDelta.y < 0)
cardDeck.navigateTo(cardDeck.currentIndex + 1);
}
}
}
}
}
@@ -0,0 +1,101 @@
import qs.Commons
import qs.Widgets
import QtQuick
Rectangle {
id: loadingBar
property bool loading: pending > 0
required property int pending
property real progress: total > 0 ? 1 - (pending / total) : 0
required property int total
color: Qt.alpha(Color.mSurface, 0.9)
height: upperRow.height + Style.margin2S + progressBar.height
radius: Style.radiusXS
visible: loading
width: upperRow.width + Style.margin2L
Behavior on opacity {
NumberAnimation {
duration: 300
easing.type: Easing.OutCubic
}
}
Row {
id: upperRow
anchors.centerIn: parent
anchors.verticalCenterOffset: -progressBar.height / 2
Item {
anchors.verticalCenter: parent.verticalCenter
Repeater {
model: 3
Rectangle {
property real angle: index * (2 * Math.PI / 3)
color: Color.mPrimary
height: Style.marginXS
radius: Style.marginXXS
width: Style.marginXS
x: Style.marginS + Style.marginS * Math.cos(angle + spinAnimation.value)
y: Style.marginS + Style.marginS * Math.sin(angle + spinAnimation.value)
NumberAnimation on opacity {
duration: 600
from: 0.3
loops: Animation.Infinite
running: loadingBar.loading
to: 1
}
}
}
NumberAnimation {
id: spinAnimation
property real value: 0
duration: 1200
from: 0
loops: Animation.Infinite
property: "value"
running: loadingBar.loading
target: spinAnimation
to: 2 * Math.PI
}
}
NText {
text: `${root.pluginApi?.tr("widget.generate-thumbs-message")} ${String(Math.max(loadingBar.total - loadingBar.pending, 0)).padStart(String(loadingBar.total).length, " ")} / ${loadingBar.total}`
}
}
Rectangle {
id: progressBar
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
color: Qt.alpha(Color.mOnSurface, 0.1)
height: Style.marginS
radius: loadingBar.radius
Rectangle {
color: Color.mPrimary
height: parent.height
radius: parent.radius
width: parent.width * loadingBar.progress
Behavior on width {
NumberAnimation {
duration: 200
easing.type: Easing.OutCubic
}
}
}
}
}
@@ -0,0 +1,75 @@
import QtQuick
import qs.Commons
import qs.Widgets
Row {
id: hint
required property string keys
required property string label
spacing: Style.marginXS
Row {
anchors.verticalCenter: parent.verticalCenter
spacing: Style.marginXXS
Repeater {
model: hint.keys.split(" / ")
Row {
required property int index
required property var modelData
anchors.verticalCenter: parent.verticalCenter
spacing: Style.marginXXS
// Separator between keys
NText {
anchors.verticalCenter: parent.verticalCenter
color: Qt.alpha(Color.mOnSurface, 0.3)
font.pointSize: Style.fontSizeXXS
text: root.pluginApi?.tr("shortcuts.key-separator")
visible: index > 0
}
// Keycap
Rectangle {
anchors.verticalCenter: parent.verticalCenter
border.color: Qt.alpha(Color.mOnSurface, 0.2)
border.width: Style.borderS
color: Qt.alpha(Color.mOnSurface, 0.08)
height: Style.marginL + 2
radius: Style.radiusXS
width: Math.max(Style.marginL + 2, keycapText.width + Style.marginS)
Rectangle {
anchors.bottomMargin: 2
anchors.fill: parent
border.color: Qt.alpha(Color.mOnSurface, 0.15)
border.width: Style.borderS
color: Qt.alpha(Color.mOnSurface, 0.05)
radius: Style.radiusXS
NText {
id: keycapText
anchors.centerIn: parent
color: Qt.alpha(Color.mOnSurface, 0.6)
font.bold: true
font.pointSize: Style.fontSizeXXS
text: modelData.trim()
}
}
}
}
}
}
NText {
anchors.verticalCenter: parent.verticalCenter
color: Qt.alpha(Color.mOnSurface, 0.4)
font.pointSize: Style.fontSizeXXS
text: hint.label
}
}
+203
View File
@@ -0,0 +1,203 @@
import QtQuick
import qs.Commons
import qs.Widgets
Rectangle {
id: sideBar
property bool expanded: false
property bool hideHelp: true
color: Qt.alpha(Color.mSurface, 0.9)
radius: Style.radiusS
width: expanded ? shortcutColumn.width + Style.margin2L : !hideHelp ? collapsedColumn.width + Style.margin2L : 0
height: expanded ? shortcutColumn.height + Style.margin2L : !hideHelp ? collapsedColumn.height + Style.margin2L : 0
Behavior on width {
NumberAnimation {
duration: 150
easing.type: Easing.OutCubic
}
}
Behavior on height {
NumberAnimation {
duration: 150
easing.type: Easing.OutCubic
}
}
clip: true
Column {
id: collapsedColumn
anchors.centerIn: parent
spacing: Style.marginS
visible: !sideBar.expanded && !sideBar.hideHelp
ShortcutHint {
keys: "?"
label: root.pluginApi?.tr("shortcuts.label.help-title")
}
}
Column {
id: shortcutColumn
anchors.centerIn: parent
spacing: Style.marginXS
visible: sideBar.expanded
// Navigation
NText {
color: Qt.alpha(Color.mOnSurface, 0.35)
font.bold: true
font.pointSize: Style.fontSizeXXS
text: root.pluginApi?.tr("shortcuts.header.navigation")
}
ShortcutHint {
keys: "J / K"
label: root.pluginApi?.tr("shortcuts.label.navigate")
}
ShortcutHint {
keys: "H / L"
label: root.pluginApi?.tr("shortcuts.label.jump")
}
ShortcutHint {
keys: "R"
label: root.pluginApi?.tr("shortcuts.label.shuffle")
}
// Separator
Rectangle {
color: Qt.alpha(Color.mOnSurface, 0.1)
height: 1
width: shortcutColumn.width
}
// Actions
NText {
color: Qt.alpha(Color.mOnSurface, 0.35)
font.bold: true
font.pointSize: Style.fontSizeXXS
text: root.pluginApi?.tr("shortcuts.header.actions")
}
ShortcutHint {
keys: "ENTER"
label: root.pluginApi?.tr("shortcuts.label.apply-quit")
}
ShortcutHint {
keys: "SPACE"
label: root.pluginApi?.tr("shortcuts.label.apply")
}
ShortcutHint {
keys: "ESC / Q"
label: root.pluginApi?.tr("shortcuts.label.quit")
}
// Separator
Rectangle {
color: Qt.alpha(Color.mOnSurface, 0.1)
height: 1
width: shortcutColumn.width
}
// Filters
NText {
color: Qt.alpha(Color.mOnSurface, 0.35)
font.bold: true
font.pointSize: Style.fontSizeXXS
text: root.pluginApi?.tr("shortcuts.header.filters")
}
ShortcutHint {
keys: "A"
label: root.pluginApi?.tr("shortcuts.label.filter-all")
}
ShortcutHint {
keys: "I"
label: root.pluginApi?.tr("shortcuts.label.filter-images")
}
ShortcutHint {
keys: "V"
label: root.pluginApi?.tr("shortcuts.label.filter-videos")
}
ShortcutHint {
keys: "F"
label: root.pluginApi?.tr("shortcuts.label.filter-colors")
}
// Separator
Rectangle {
color: Qt.alpha(Color.mOnSurface, 0.1)
height: 1
width: shortcutColumn.width
}
// View
NText {
color: Qt.alpha(Color.mOnSurface, 0.35)
font.bold: true
font.pointSize: Style.fontSizeXXS
text: root.pluginApi?.tr("shortcuts.header.view")
}
ShortcutHint {
keys: "T"
label: root.pluginApi?.tr("shortcuts.label.top-bar")
}
ShortcutHint {
keys: "P"
label: root.pluginApi?.tr("shortcuts.label.live-preview")
}
// Separator
Rectangle {
color: Qt.alpha(Color.mOnSurface, 0.1)
height: 1
width: shortcutColumn.width
}
// Layout
NText {
color: Qt.alpha(Color.mOnSurface, 0.35)
font.bold: true
font.pointSize: Style.fontSizeXXS
text: root.pluginApi?.tr("shortcuts.header.layout")
}
ShortcutHint {
keys: "SHIFT + H / L"
label: root.pluginApi?.tr("shortcuts.label.center-height")
}
ShortcutHint {
keys: "SHIFT + J / K"
label: root.pluginApi?.tr("shortcuts.label.center-width")
}
ShortcutHint {
keys: "SHIFT + N / P"
label: root.pluginApi?.tr("shortcuts.label.cards-shown")
}
ShortcutHint {
keys: "CTRL + J / K"
label: root.pluginApi?.tr("shortcuts.label.cards-spacing")
}
ShortcutHint {
keys: "CTRL + H / L"
label: root.pluginApi?.tr("shortcuts.label.cards-width")
}
// Separator
Rectangle {
color: Qt.alpha(Color.mOnSurface, 0.1)
height: 1
width: shortcutColumn.width
}
ShortcutHint {
keys: "CTRL + S"
label: root.pluginApi?.tr("shortcuts.label.save")
}
ShortcutHint {
keys: "?"
label: root.pluginApi?.tr("shortcuts.label.hide")
}
}
}
@@ -0,0 +1,275 @@
import "Utils.js" as Utils
import QtQuick
import Quickshell.Io
import Qt.labs.folderlistmodel
Item {
id: service
property int maxThumbnailJobs: 4
property var pendingJobs: []
property int activeJobs: 0
required property string cacheDir
required property var imageFilter
required property var videoFilter
required property string wallpaperDir
property var colorOrder: ["Red", "Orange", "Green", "Teal", "Blue", "Purple", "Pink", "Monochrome"]
property var colorOrderColors: ["#FF4500", "#FFA500", "#32CD32", "#2EC4B6", "#1E90FF", "#8A2BE2", "#FF69B4", "#A9A9A9"]
property int fileCount: files.length
property var files: []
property bool loading: true
property int pendingProcesses: 0
property int thumbnailRevision: 0
signal ready
function toLocalPath(urlStr) {
return String(urlStr).replace(/^file:\/\//, "");
}
FolderListModel {
id: thumbnailModel
folder: service.wallpaperDir ? Qt.resolvedUrl("file://" + service.wallpaperDir) : ""
nameFilters: Utils.nameFilters(service.imageFilter, service.videoFilter)
showDirs: false
sortField: FolderListModel.Name
onStatusChanged: {
if (status === FolderListModel.Ready) {
service.createThumbnails();
}
}
}
function createThumbnails() {
var proc = processComponent.createObject(null, {
command: ["mkdir", "-p", cacheDir]
});
proc.running = true;
var items = [];
var jobs = [];
for (var i = 0; i < thumbnailModel.count; i++) {
(function (idx) {
var filePath = toLocalPath(thumbnailModel.get(idx, "filePath"));
var fileName = thumbnailModel.get(idx, "fileName");
var isVid = Utils.isVideo(fileName, service.videoFilter);
var thumbName = isVid ? fileName + ".jpg" : fileName;
var thumbnailPath = cacheDir + "/" + thumbName;
var thumbnailCmd = isVid ? videoToThumbnailCmd(filePath, thumbnailPath) : imageToThumbnailCmd(filePath, thumbnailPath);
var hexCmd = thumbnailHexValueCmd(thumbnailPath);
const script = `
[ -f "${thumbnailPath}"* ] && exit 0
${thumbnailCmd}
mv "${thumbnailPath}" "${thumbnailPath}__x$(${hexCmd})"
`;
jobs.push({
script: script,
thumbnailPath: thumbnailPath
});
items.push({});
})(i);
}
pendingJobs = jobs;
activeJobs = 0;
processNextJob();
if (thumbnailModel.count === 0) {
service.loading = false;
}
files = items;
}
function processNextJob() {
while (activeJobs < maxThumbnailJobs && pendingJobs.length > 0) {
var job = pendingJobs.shift();
activeJobs++;
service.pendingProcesses++;
createSingleThumbnail(job, function() {
activeJobs--;
processNextJob();
});
}
}
function createSingleThumbnail(job, onFinished) {
var proc = processComponent.createObject(null, {
command: ["bash", "-c", job.script]
});
proc.exited.connect(function () {
service.pendingProcesses--;
service.thumbnailRevision++;
if (service.pendingProcesses === 0) {
filesModel.running = true;
}
proc.destroy();
if (onFinished) onFinished();
});
proc.running = true;
}
function imageToThumbnailCmd(filePath, thumbnailPath) {
return `magick "${filePath}" \
-resize x500 \
-quality 95 \
"${thumbnailPath}"
`;
}
function videoToThumbnailCmd(filePath, thumbnailPath) {
return `ffmpeg -y -i \
"${filePath}" \
-vf "select=eq(n\\,0),scale=-1:1080" \
-frames:v 1 \
-q:v 2 \
"${thumbnailPath}" </dev/null 2>/dev/null`;
}
function thumbnailHexValueCmd(thumbnailPath) {
return `magick "${thumbnailPath}" \
-resize "1x1^" \
-gravity center \
-extent 1x1 \
-depth 8 \
-format "%[hex:p{0,0}]" info:- 2>/dev/null \
| grep -oE '[0-9A-Fa-f]{6}' \
| head -n 1`;
}
FolderListModel {
id: filesModel
nameFilters: ["*__x*"]
showDirs: false
sortField: FolderListModel.Name
property bool running: false
onRunningChanged: {
if (running)
folder = Qt.resolvedUrl("file://" + service.cacheDir);
}
onStatusChanged: {
if (status === FolderListModel.Ready && running) {
service.buildFileList();
running = false;
}
}
}
function buildFileList() {
// Ground truth needed to avoid showing thumbnails for files
// that are no longer in the wallpaper directory.
var existingFiles = new Set();
for (let j = 0; j < thumbnailModel.count; j++) {
existingFiles.add(thumbnailModel.get(j, "fileName"));
}
var items = [];
for (let i = 0; i < filesModel.count; i++) {
const filePath = toLocalPath(filesModel.get(i, "filePath"));
const fileName = filesModel.get(i, "fileName");
const idx = fileName.lastIndexOf("__x");
if (idx === -1)
continue;
const thumbBase = fileName.substring(0, idx);
const hexColor = fileName.substring(idx + 3);
// Video thumbnails are named <original>.<vidext>.jpg__x<hex>
// Strip trailing .jpg to recover the original video filename.
var isVid = thumbBase.toLowerCase().endsWith(".jpg") && Utils.isVideo(thumbBase.substring(0, thumbBase.lastIndexOf(".")), service.videoFilter);
var wallpaperName = isVid ? thumbBase.substring(0, thumbBase.lastIndexOf(".")) : thumbBase;
if (!existingFiles.has(wallpaperName)) {
continue;
}
items.push({
fileName: wallpaperName,
filePath: wallpaperDir + "/" + wallpaperName,
thumbnail: filePath,
hexCode: hexColor,
filterColor: getFilterColor(hexColor),
isVideo: isVid
});
}
items.sort((a, b) => colorOrder.indexOf(a.filterColor) - colorOrder.indexOf(b.filterColor));
files = items;
service.loading = false;
service.ready();
}
function getFilterColor(hexColor) {
if (!hexColor)
return "Monochrome";
const cleaned = String(hexColor).trim().replace(/x/g, '').substring(0, 6);
if (cleaned.length !== 6)
return "Monochrome";
const r = parseInt(cleaned.substring(0, 2), 16) / 255;
const g = parseInt(cleaned.substring(2, 4), 16) / 255;
const b = parseInt(cleaned.substring(4, 6), 16) / 255;
if ([r, g, b].some(isNaN))
return "Monochrome";
const max = Math.max(r, g, b);
const min = Math.min(r, g, b);
const d = max - min;
let h = 0;
let s = max === 0 ? 0 : d / max;
let v = max;
if (d !== 0) {
if (max === r)
h = (g - b) / d + (g < b ? 6 : 0);
else if (max === g)
h = (b - r) / d + 2;
else
h = (r - g) / d + 4;
h = (h / 6) * 360;
}
if (s < 0.15 || v < 0.10)
return "Monochrome";
if (h >= 345 || h < 15)
return "Red";
if (h < 50)
return "Orange";
if (h < 160)
return "Green";
if (h < 200)
return "Teal";
if (h < 260)
return "Blue";
if (h < 315)
return "Purple";
if (h < 345)
return "Pink";
return "Monochrome";
}
Component {
id: processComponent
Process {}
}
}
+418
View File
@@ -0,0 +1,418 @@
import qs.Commons
import qs.Widgets
import QtQuick
Rectangle {
id: topBar
required property int animationDuration
required property var availableColors
required property var colorOrder
required property var colorOrderColors
required property string currentCardColor
required property int currentIndex
property real entryOffset: parent.width / 2
required property int filteredCount
required property bool livePreview
required property var pluginApi
required property string selectedColorFilter
required property string selectedFilter
required property real shearFactor
signal colorFilterSelected(string key)
signal filterSelected(string key)
signal livePreviewToggled
signal shuffleRequested
function flashShuffle() {
shuffleBtn.flash();
}
color: Color.mSurface
Behavior on entryOffset {
NumberAnimation {
duration: topBar.animationDuration
easing.overshoot: 1.0
easing.type: Easing.OutBack
}
}
transform: Matrix4x4 {
property real s: topBar.shearFactor
matrix: Qt.matrix4x4(1, s, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)
}
Component.onCompleted: entryOffset = 0
// ── Inline Components ──
component TButton: Rectangle {
id: btn
property color accentColor: active ? Color.mOnSurface : Color.mOnSurfaceVariant
property bool active: false
property string hotkey: ""
property string icon: ""
property string label: ""
property bool pulsing: false
signal clicked
border.color: active ? Qt.alpha(accentColor, Style.opacityMedium) : Qt.alpha(Color.mOutline, 0.3)
border.width: Style.borderS
color: active ? Qt.alpha(accentColor, 0.15) : Qt.alpha(Color.mOnSurface, 0.06)
height: Style.margin2L
radius: Style.radiusM
width: btnRow.width + Style.margin2M
Behavior on border.color {
ColorAnimation {
duration: Style.animationFast
}
}
Behavior on color {
ColorAnimation {
duration: Style.animationFast
}
}
Row {
id: btnRow
anchors.centerIn: parent
spacing: Style.marginS
PulsingDot {
anchors.verticalCenter: parent.verticalCenter
pulsing: btn.pulsing
visible: btn.pulsing
}
NIcon {
color: btn.accentColor
icon: btn.icon
visible: btn.icon !== ""
}
NText {
anchors.verticalCenter: parent.verticalCenter
color: btn.accentColor
font.pointSize: Style.fontSizeXS
text: btn.label
visible: btn.label !== ""
}
Rectangle {
anchors.verticalCenter: parent.verticalCenter
border.color: Qt.alpha(btn.accentColor, 0.35)
border.width: Style.borderM
color: Qt.alpha(btn.accentColor, 0.12)
height: Style.marginL + 2
opacity: 0.25
radius: Style.radiusXS
visible: btn.hotkey !== ""
width: Style.marginL + 5
Rectangle {
anchors.bottomMargin: 2
anchors.fill: parent
border.color: Qt.alpha(btn.accentColor, 0.25)
border.width: Style.borderS
color: Qt.alpha(btn.accentColor, 0.1)
radius: Style.radiusXS
NText {
anchors.centerIn: parent
color: Qt.alpha(btn.accentColor, 0.7)
font.bold: true
font.pointSize: Style.fontSizeXXS
text: btn.hotkey
}
}
}
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: btn.clicked()
}
}
component CButton: Rectangle {
id: colorBtn
property bool active: false
property bool available: true
property bool current: false
required property color faceColor
signal clicked
border.color: active ? Color.mOnSurface : Qt.alpha(Color.mOutline, 0.3)
border.width: Style.borderS
color: faceColor
height: Style.margin2L
opacity: active ? 1.0 : available ? 0.4 : 0.12
radius: Style.radiusM
width: height
Behavior on opacity {
NumberAnimation {
duration: Style.animationFast
}
}
Behavior on border.color {
ColorAnimation {
duration: Style.animationFast
}
}
Rectangle {
anchors.bottom: parent.bottom
anchors.bottomMargin: -height - 2
anchors.horizontalCenter: parent.horizontalCenter
color: colorBtn.current ? colorBtn.faceColor : "transparent"
height: 3
radius: height / 2
width: colorBtn.current ? parent.width * 0.6 : 0
Behavior on width {
NumberAnimation {
duration: Style.animationFast
easing.type: Easing.OutCubic
}
}
Behavior on color {
ColorAnimation {
duration: Style.animationFast
}
}
}
NText {
anchors.centerIn: parent
color: Color.mOnSurface
font.bold: true
font.pointSize: Style.fontSizeS
text: topBar.pluginApi?.tr("buttons.color-na")
visible: !colorBtn.available
}
MouseArea {
anchors.fill: parent
cursorShape: colorBtn.available ? Qt.PointingHandCursor : Qt.ForbiddenCursor
enabled: colorBtn.available
onClicked: colorBtn.clicked()
}
}
component SButton: Item {
id: shuffleItem
property string hotkey: ""
property string icon: ""
property string label: ""
signal clicked
function flash() {
flashAnim.restart();
}
height: innerBtn.height
width: innerBtn.width
TButton {
id: innerBtn
hotkey: shuffleItem.hotkey
icon: shuffleItem.icon
label: shuffleItem.label
onClicked: {
shuffleItem.clicked();
shuffleItem.flash();
}
}
Rectangle {
id: flashOverlay
anchors.fill: innerBtn
color: Color.mPrimary
opacity: 0
radius: innerBtn.radius
}
SequentialAnimation {
id: flashAnim
NumberAnimation {
duration: 80
easing.type: Easing.OutCubic
property: "opacity"
target: flashOverlay
to: 0.3
}
NumberAnimation {
duration: 300
easing.type: Easing.OutCubic
property: "opacity"
target: flashOverlay
to: 0
}
}
}
component PulsingDot: Rectangle {
id: root
property color dotColor: Color.mError
property bool pulsing: false
color: dotColor
height: Style.marginS
radius: Style.marginXXXS
width: Style.marginS
SequentialAnimation {
id: pulseAnimation
loops: Animation.Infinite
running: root.pulsing
onRunningChanged: {
if (!running)
root.opacity = 1.0;
}
NumberAnimation {
duration: 800
easing.type: Easing.InOutSine
from: 1.0
property: "opacity"
target: root
to: 0.3
}
NumberAnimation {
duration: 800
easing.type: Easing.InOutSine
from: 0.3
property: "opacity"
target: root
to: 1.0
}
}
}
// ── Left ──
NText {
anchors.left: parent.left
anchors.leftMargin: Style.marginL
anchors.verticalCenter: parent.verticalCenter
text: (topBar.currentIndex + 1) + " / " + topBar.filteredCount
}
// ── Center ──
Row {
anchors.centerIn: parent
spacing: Style.marginXS
Repeater {
model: [
{
key: "all",
label: topBar.pluginApi?.tr("buttons.all"),
icon: "wallpaper",
hotkey: "A"
},
{
key: "images",
label: topBar.pluginApi?.tr("buttons.images"),
icon: "image",
hotkey: "I"
},
{
key: "videos",
label: topBar.pluginApi?.tr("buttons.videos"),
icon: "video",
hotkey: "V"
}
]
TButton {
required property var modelData
active: topBar.selectedFilter === modelData.key
hotkey: modelData.hotkey
icon: modelData.icon
label: modelData.label || ""
onClicked: topBar.filterSelected(modelData.key)
}
}
Rectangle {
anchors.verticalCenter: parent.verticalCenter
color: Qt.alpha(Color.mOnSurface, 0.15)
height: parent.height * 0.5
width: 1
}
Repeater {
model: topBar.colorOrder
CButton {
required property int index
required property string modelData
active: topBar.selectedColorFilter === modelData
anchors.verticalCenter: parent.verticalCenter
available: topBar.availableColors.indexOf(modelData) !== -1
current: topBar.selectedColorFilter === "" && topBar.currentCardColor === modelData
faceColor: topBar.colorOrderColors[index]
onClicked: {
if (active)
topBar.colorFilterSelected("");
else
topBar.colorFilterSelected(modelData);
}
}
}
}
// ── Right ──
Row {
anchors.right: parent.right
anchors.rightMargin: Style.marginL
anchors.verticalCenter: parent.verticalCenter
spacing: Style.marginXS
SButton {
id: shuffleBtn
hotkey: "R"
icon: "arrows-random"
label: topBar.pluginApi?.tr("buttons.shuffle")
onClicked: topBar.shuffleRequested()
}
TButton {
accentColor: topBar.livePreview ? Color.mTertiary : Color.mOnSurfaceVariant
active: topBar.livePreview
hotkey: "P"
label: topBar.pluginApi?.tr("buttons.live-preview")
pulsing: topBar.livePreview
onClicked: topBar.livePreviewToggled()
}
}
}
+37
View File
@@ -0,0 +1,37 @@
function getExtension(fileName) {
return fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase();
}
function isVideo(fileName, filterVideos) {
return filterVideos.indexOf(getExtension(fileName)) !== -1;
}
function isImage(fileName, filterImages) {
return filterImages.indexOf(getExtension(fileName)) !== -1;
}
function nameFilters(filterImages, filterVideos) {
return (filterImages || []).concat(filterVideos || []).map((ext) => "*." + ext);
}
var mpvpaperKill = "killall -9 mpvpaper 2>/dev/null || true";
var mpvpaperOptions = [
"loop",
"--no-audio",
"--hwdec=auto",
"--profile=high-quality",
"--video-sync=display-resample",
"--interpolation",
"--tscale=oversample"
].join(" ");
function mpvpaperRun(filePath) {
return "mpvpaper -o '" + mpvpaperOptions + "' '*' \"" + filePath + "\" >/dev/null 2>&1 & disown";
}
function wallpaperCommand(entry) {
if (entry.isVideo)
return mpvpaperKill + "; " + mpvpaperRun(entry.filePath);
return mpvpaperKill;
}