feat: launcher wallpaper selector
This commit is contained in:
parent
d01194d1fc
commit
00d3c1a472
13 changed files with 429 additions and 105 deletions
|
|
@ -5,11 +5,14 @@ import QtQuick
|
|||
|
||||
Singleton {
|
||||
readonly property int maxShown: 8
|
||||
readonly property int maxWallpapers: 9 // Warning: even numbers look bad
|
||||
readonly property string actionPrefix: ">"
|
||||
readonly property Sizes sizes: Sizes {}
|
||||
|
||||
component Sizes: QtObject {
|
||||
readonly property int width: 600
|
||||
readonly property int itemWidth: 600
|
||||
readonly property int itemHeight: 57
|
||||
readonly property int wallpaperWidth: 280
|
||||
readonly property int wallpaperHeight: 200
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,10 +9,12 @@ PaddedRect {
|
|||
required property Actions.Action modelData
|
||||
required property var list
|
||||
|
||||
implicitWidth: ListView.view.width
|
||||
implicitHeight: LauncherConfig.sizes.itemHeight
|
||||
padding: [Appearance.padding.smaller, Appearance.padding.larger]
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
StateLayer {
|
||||
radius: Appearance.rounding.normal
|
||||
|
||||
|
|
|
|||
|
|
@ -10,10 +10,12 @@ PaddedRect {
|
|||
required property DesktopEntry modelData
|
||||
required property Scope launcher
|
||||
|
||||
implicitWidth: ListView.view.width
|
||||
implicitHeight: LauncherConfig.sizes.itemHeight
|
||||
padding: [Appearance.padding.smaller, Appearance.padding.normal]
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
StateLayer {
|
||||
radius: Appearance.rounding.normal
|
||||
|
||||
|
|
|
|||
|
|
@ -30,17 +30,12 @@ ListView {
|
|||
onValuesChanged: root.currentIndex = 0
|
||||
}
|
||||
|
||||
clip: true
|
||||
spacing: Appearance.spacing.small
|
||||
orientation: Qt.Vertical
|
||||
implicitHeight: (LauncherConfig.sizes.itemHeight + spacing) * Math.min(LauncherConfig.maxShown, count) - spacing
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.margins: root.padding
|
||||
|
||||
highlightMoveDuration: Appearance.anim.durations.normal
|
||||
highlightResizeDuration: 0
|
||||
|
||||
highlight: StyledRect {
|
||||
radius: Appearance.rounding.normal
|
||||
|
|
@ -111,13 +106,6 @@ ListView {
|
|||
}
|
||||
}
|
||||
|
||||
Behavior on implicitHeight {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.large
|
||||
easing.bezierCurve: Appearance.anim.curves.emphasizedDecel
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on isAction {
|
||||
SequentialAnimation {
|
||||
ParallelAnimation {
|
||||
|
|
|
|||
|
|
@ -5,13 +5,12 @@ import QtQuick.Shapes
|
|||
Shape {
|
||||
id: root
|
||||
|
||||
required property real wrapperWidth
|
||||
required property real realWrapperHeight
|
||||
readonly property int rounding: Appearance.rounding.large
|
||||
readonly property int roundingY: Math.min(rounding, realWrapperHeight / 2)
|
||||
readonly property real wrapperHeight: realWrapperHeight - 1 // Pixel issues :sob:
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
preferredRendererType: Shape.CurveRenderer
|
||||
|
|
@ -41,7 +40,7 @@ Shape {
|
|||
radiusY: root.roundingY
|
||||
}
|
||||
PathLine {
|
||||
x: wrapper.width - root.rounding * 2
|
||||
x: root.wrapperWidth - root.rounding * 2
|
||||
}
|
||||
PathArc {
|
||||
relativeX: root.rounding
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
pragma ComponentBehavior: Bound
|
||||
|
||||
import "root:/widgets"
|
||||
import "root:/services"
|
||||
import "root:/config"
|
||||
|
|
@ -13,7 +15,7 @@ Item {
|
|||
readonly property int spacing: Appearance.spacing.normal
|
||||
readonly property int rounding: Appearance.rounding.large
|
||||
|
||||
implicitWidth: LauncherConfig.sizes.width
|
||||
implicitWidth: listWrapper.width + padding * 2
|
||||
implicitHeight: search.height + listWrapper.height + padding * 2 + spacing
|
||||
|
||||
anchors.bottom: parent.bottom
|
||||
|
|
@ -24,26 +26,23 @@ Item {
|
|||
|
||||
color: Appearance.alpha(Appearance.colours.m3surfaceContainerHigh, true)
|
||||
radius: root.rounding
|
||||
implicitHeight: Math.max(empty.height, list.height) + root.padding * 2
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
implicitWidth: list.width + root.padding * 2
|
||||
implicitHeight: list.height + root.padding * 2
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.bottom: search.top
|
||||
anchors.bottomMargin: root.spacing
|
||||
anchors.margins: root.padding
|
||||
|
||||
AppList {
|
||||
ContentList {
|
||||
id: list
|
||||
|
||||
padding: root.padding
|
||||
search: search
|
||||
launcher: root.launcher
|
||||
}
|
||||
|
||||
EmptyIndicator {
|
||||
id: empty
|
||||
|
||||
empty: list.count === 0
|
||||
search: search
|
||||
padding: root.padding
|
||||
spacing: root.spacing
|
||||
rounding: root.rounding
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -68,18 +67,22 @@ Item {
|
|||
}
|
||||
|
||||
onAccepted: {
|
||||
if (list.currentItem) {
|
||||
if (list.isAction)
|
||||
list.currentItem.modelData.onClicked(list);
|
||||
else {
|
||||
Apps.launch(list.currentItem?.modelData);
|
||||
const currentItem = list.currentList?.currentItem;
|
||||
if (currentItem) {
|
||||
if (list.showWallpapers) {
|
||||
// TODO
|
||||
console.log(currentItem.modelData.path);
|
||||
} else if (text.startsWith(LauncherConfig.actionPrefix)) {
|
||||
currentItem.modelData.onClicked(list.currentList);
|
||||
} else {
|
||||
Apps.launch(currentItem.modelData);
|
||||
root.launcher.launcherVisible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onUpPressed: list.decrementCurrentIndex()
|
||||
Keys.onDownPressed: list.incrementCurrentIndex()
|
||||
Keys.onUpPressed: list.currentList?.decrementCurrentIndex()
|
||||
Keys.onDownPressed: list.currentList?.incrementCurrentIndex()
|
||||
|
||||
Keys.onEscapePressed: root.launcher.launcherVisible = false
|
||||
|
||||
|
|
@ -91,7 +94,9 @@ Item {
|
|||
search.forceActiveFocus();
|
||||
else {
|
||||
search.text = "";
|
||||
list.currentIndex = 0;
|
||||
const current = list.currentList;
|
||||
if (current)
|
||||
current.currentIndex = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
193
modules/launcher/ContentList.qml
Normal file
193
modules/launcher/ContentList.qml
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
pragma ComponentBehavior: Bound
|
||||
|
||||
import "root:/widgets"
|
||||
import "root:/services"
|
||||
import "root:/config"
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
required property Scope launcher
|
||||
required property TextField search
|
||||
required property int padding
|
||||
required property int spacing
|
||||
required property int rounding
|
||||
|
||||
property bool showWallpapers: search.text.startsWith(`${LauncherConfig.actionPrefix}wallpaper `)
|
||||
property var currentList: (showWallpapers ? wallpaperList : appList).item
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: root.padding
|
||||
|
||||
clip: true
|
||||
state: showWallpapers ? "wallpapers" : "apps"
|
||||
|
||||
states: [
|
||||
State {
|
||||
name: "apps"
|
||||
|
||||
PropertyChanges {
|
||||
root.implicitWidth: LauncherConfig.sizes.itemWidth
|
||||
root.implicitHeight: Math.max(empty.height, appList.height)
|
||||
appList.active: true
|
||||
}
|
||||
|
||||
AnchorChanges {
|
||||
anchors.left: root.parent.left
|
||||
anchors.right: root.parent.right
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "wallpapers"
|
||||
|
||||
PropertyChanges {
|
||||
root.implicitWidth: Math.max(LauncherConfig.sizes.itemWidth, wallpaperList.width)
|
||||
root.implicitHeight: LauncherConfig.sizes.wallpaperHeight
|
||||
wallpaperList.active: true
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
transitions: Transition {
|
||||
from: "*"
|
||||
to: "*"
|
||||
|
||||
SequentialAnimation {
|
||||
NumberAnimation {
|
||||
target: root
|
||||
property: "opacity"
|
||||
from: 1
|
||||
to: 0
|
||||
duration: Appearance.anim.durations.small
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Appearance.anim.curves.standard
|
||||
}
|
||||
PropertyAction {
|
||||
targets: [appList, wallpaperList]
|
||||
properties: "active"
|
||||
}
|
||||
ParallelAnimation {
|
||||
NumberAnimation {
|
||||
target: root
|
||||
properties: "implicitWidth,implicitHeight"
|
||||
duration: Appearance.anim.durations.large
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Appearance.anim.curves.emphasized
|
||||
}
|
||||
NumberAnimation {
|
||||
target: root
|
||||
property: "opacity"
|
||||
from: 0
|
||||
to: 1
|
||||
duration: Appearance.anim.durations.large
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Appearance.anim.curves.standard
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: appList
|
||||
|
||||
active: false
|
||||
asynchronous: true
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
sourceComponent: AppList {
|
||||
padding: root.padding
|
||||
search: root.search
|
||||
launcher: root.launcher
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: wallpaperList
|
||||
|
||||
active: false
|
||||
asynchronous: true
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
sourceComponent: WallpaperList {
|
||||
search: root.search
|
||||
launcher: root.launcher
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: empty
|
||||
|
||||
opacity: root.currentList.count === 0 ? 1 : 0
|
||||
scale: root.currentList.count === 0 ? 1 : 0.5
|
||||
|
||||
implicitWidth: icon.width + text.width + Appearance.spacing.small
|
||||
implicitHeight: icon.height
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
MaterialIcon {
|
||||
id: icon
|
||||
|
||||
text: "manage_search"
|
||||
color: Appearance.colours.m3outline
|
||||
font.pointSize: Appearance.font.size.extraLarge
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: text
|
||||
|
||||
anchors.left: icon.right
|
||||
anchors.leftMargin: Appearance.spacing.small
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
text: qsTr("No results")
|
||||
color: Appearance.colours.m3outline
|
||||
font.pointSize: Appearance.font.size.larger
|
||||
font.weight: 500
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: Appearance.anim.durations.normal
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Appearance.anim.curves.standard
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on scale {
|
||||
NumberAnimation {
|
||||
duration: Appearance.anim.durations.normal
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Appearance.anim.curves.standard
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on implicitWidth {
|
||||
NumberAnimation {
|
||||
duration: Appearance.anim.durations.large
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Appearance.anim.curves.emphasizedDecel
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on implicitHeight {
|
||||
NumberAnimation {
|
||||
duration: Appearance.anim.durations.large
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Appearance.anim.curves.emphasizedDecel
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
import "root:/widgets"
|
||||
import "root:/config"
|
||||
import QtQuick
|
||||
|
||||
Loader {
|
||||
id: root
|
||||
|
||||
required property bool empty
|
||||
|
||||
active: false
|
||||
opacity: 0
|
||||
scale: 0
|
||||
asynchronous: true
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
sourceComponent: Item {
|
||||
implicitWidth: childrenRect.width
|
||||
implicitHeight: icon.height
|
||||
|
||||
MaterialIcon {
|
||||
id: icon
|
||||
|
||||
text: "manage_search"
|
||||
color: Appearance.colours.m3outline
|
||||
font.pointSize: Appearance.font.size.extraLarge
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
anchors.left: icon.right
|
||||
anchors.leftMargin: Appearance.spacing.small
|
||||
anchors.verticalCenter: icon.verticalCenter
|
||||
|
||||
text: qsTr("No matching apps found")
|
||||
color: Appearance.colours.m3outline
|
||||
font.pointSize: Appearance.font.size.larger
|
||||
font.weight: 500
|
||||
}
|
||||
}
|
||||
|
||||
states: State {
|
||||
name: "visible"
|
||||
when: root.empty
|
||||
|
||||
PropertyChanges {
|
||||
root.active: true
|
||||
root.opacity: 1
|
||||
root.scale: 1
|
||||
}
|
||||
}
|
||||
|
||||
transitions: Transition {
|
||||
NumberAnimation {
|
||||
properties: "opacity,scale"
|
||||
duration: Appearance.anim.durations.large
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Appearance.anim.curves.standard
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -21,23 +21,31 @@ Scope {
|
|||
keyboardFocus: root.launcherVisible ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None
|
||||
visible: wrapper.shouldBeVisible
|
||||
|
||||
width: content.width + bg.rounding * 2
|
||||
mask: Region {
|
||||
item: wrapper
|
||||
}
|
||||
|
||||
anchors.top: true
|
||||
anchors.left: true
|
||||
anchors.bottom: true
|
||||
anchors.right: true
|
||||
|
||||
Background {
|
||||
id: bg
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
wrapperWidth: wrapper.width
|
||||
realWrapperHeight: Math.min(wrapper.height, content.height)
|
||||
}
|
||||
|
||||
Wrapper {
|
||||
id: wrapper
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
implicitWidth: content.width + bg.rounding * 2
|
||||
|
||||
launcherVisible: root.launcherVisible
|
||||
contentHeight: content.height
|
||||
|
||||
|
|
|
|||
91
modules/launcher/WallpaperItem.qml
Normal file
91
modules/launcher/WallpaperItem.qml
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
import "root:/widgets"
|
||||
import "root:/services"
|
||||
import "root:/config"
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
import QtQuick.Effects
|
||||
|
||||
StyledRect {
|
||||
id: root
|
||||
|
||||
required property Wallpapers.Wallpaper modelData
|
||||
|
||||
scale: PathView.isCurrentItem ? 1 : PathView.onPath ? 0.8 : 0
|
||||
opacity: PathView.onPath ? 1 : 0
|
||||
z: PathView.isCurrentItem ? 1 : 0
|
||||
|
||||
implicitWidth: image.width + Appearance.padding.larger * 2
|
||||
implicitHeight: image.height + label.height + Appearance.spacing.small / 2 + Appearance.padding.normal * 2
|
||||
|
||||
StateLayer {
|
||||
radius: Appearance.rounding.normal
|
||||
|
||||
function onClicked(): void {
|
||||
console.log("clicked");
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
id: image
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
y: Appearance.padding.normal
|
||||
|
||||
visible: false
|
||||
source: `file://${root.modelData.path}`
|
||||
asynchronous: true
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
smooth: !root.PathView.view.moving
|
||||
|
||||
width: LauncherConfig.sizes.wallpaperWidth
|
||||
height: width / 16 * 9
|
||||
sourceSize.width: width
|
||||
sourceSize.height: height
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: mask
|
||||
|
||||
layer.enabled: true
|
||||
visible: false
|
||||
anchors.fill: image
|
||||
width: image.width
|
||||
height: image.height
|
||||
radius: Appearance.rounding.normal
|
||||
}
|
||||
|
||||
MultiEffect {
|
||||
anchors.fill: image
|
||||
source: image
|
||||
maskEnabled: true
|
||||
maskSource: mask
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: label
|
||||
|
||||
anchors.top: image.bottom
|
||||
anchors.topMargin: Appearance.spacing.small / 2
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
renderType: Text.QtRendering
|
||||
text: root.modelData.name
|
||||
font.pointSize: Appearance.font.size.small
|
||||
}
|
||||
|
||||
Behavior on scale {
|
||||
NumberAnimation {
|
||||
duration: Appearance.anim.durations.normal
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Appearance.anim.curves.standard
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: Appearance.anim.durations.normal
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Appearance.anim.curves.standard
|
||||
}
|
||||
}
|
||||
}
|
||||
44
modules/launcher/WallpaperList.qml
Normal file
44
modules/launcher/WallpaperList.qml
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
import "root:/widgets"
|
||||
import "root:/services"
|
||||
import "root:/config"
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
|
||||
PathView {
|
||||
id: root
|
||||
|
||||
required property TextField search
|
||||
required property Scope launcher
|
||||
|
||||
model: ScriptModel {
|
||||
values: {
|
||||
const list = Wallpapers.fuzzyQuery(root.search.text.split(" ").slice(1).join(" "));
|
||||
if (list.length > 1 && list.length % 2 === 0)
|
||||
list.length -= 1; // Always show odd number
|
||||
return list;
|
||||
}
|
||||
onValuesChanged: root.currentIndex = 0
|
||||
}
|
||||
|
||||
implicitWidth: Math.min(LauncherConfig.maxWallpapers, count) * (LauncherConfig.sizes.wallpaperWidth * 0.8 + Appearance.padding.larger * 2)
|
||||
pathItemCount: LauncherConfig.maxWallpapers
|
||||
cacheItemCount: 4
|
||||
|
||||
snapMode: PathView.SnapToItem
|
||||
preferredHighlightBegin: 0.5
|
||||
preferredHighlightEnd: 0.5
|
||||
highlightRangeMode: PathView.StrictlyEnforceRange
|
||||
highlightMoveDuration: Appearance.anim.durations.short
|
||||
|
||||
delegate: WallpaperItem {}
|
||||
|
||||
path: Path {
|
||||
startY: root.height / 2
|
||||
|
||||
PathLine {
|
||||
x: root.width
|
||||
relativeY: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -8,8 +8,6 @@ Item {
|
|||
required property real contentHeight
|
||||
property bool shouldBeVisible
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
height: 0
|
||||
|
|
|
|||
53
services/Wallpapers.qml
Normal file
53
services/Wallpapers.qml
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
pragma Singleton
|
||||
|
||||
import "root:/utils/scripts/fuzzysort.js" as Fuzzy
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import QtQuick
|
||||
import Qt.labs.platform
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
readonly property string path: `${StandardPaths.standardLocations(StandardPaths.PicturesLocation)[0]}/Wallpapers`
|
||||
|
||||
property list<Wallpaper> list
|
||||
|
||||
readonly property list<var> preppedPaths: list.map(w => ({
|
||||
path: Fuzzy.prepare(w.path),
|
||||
wall: w
|
||||
}))
|
||||
|
||||
function fuzzyQuery(search: string): var {
|
||||
return Fuzzy.go(search, preppedPaths, {
|
||||
all: true,
|
||||
keys: ["name", "path"],
|
||||
scoreFn: r => r[0].score * 0.9 + r[1].score * 0.1
|
||||
}).map(r => r.obj.wall);
|
||||
}
|
||||
|
||||
Process {
|
||||
running: true
|
||||
command: ["fd", ".", root.path.slice(7), "-t", "f", "-e", "jpg", "-e", "jpeg", "-e", "png", "-e", "svg"]
|
||||
stdout: SplitParser {
|
||||
splitMarker: ""
|
||||
onRead: data => {
|
||||
const list = data.trim().split("\n");
|
||||
root.list = list.map(p => wallpaperComp.createObject(root, {
|
||||
path: p
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component Wallpaper: QtObject {
|
||||
required property string path
|
||||
readonly property string name: path.slice(path.lastIndexOf("/") + 1, path.lastIndexOf("."))
|
||||
}
|
||||
|
||||
Component {
|
||||
id: wallpaperComp
|
||||
|
||||
Wallpaper {}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue