// The wallpaper grid. // // A thumbnail grid rather than a file path field: choosing a background is the // one setting where the value IS the picture, and typing a path to something // you cannot see is the worst possible version of it. // // Images are loaded asynchronously and at a fraction of their real size -- // several of the candidates here are 8-12 MB, and decoding them at full // resolution to draw a 150px tile would cost more memory than the rest of the // shell put together. import QtQuick import QtQuick.Effects import qs.config import qs.services Item { id: root implicitHeight: grid.implicitHeight // Sixty tiles is two screens of wallpaper on a page that also holds // typography, window geometry and effects -- everything below it becomes // unreachable without a long scroll past pictures. Two rows by default, // all of them on request. property bool expanded: false readonly property int collapsedRows: 2 readonly property var shown: root.expanded ? Wallpaper.available : Wallpaper.available.slice(0, root.columns * root.collapsedRows) readonly property int hidden: Wallpaper.available.length - root.shown.length readonly property int columns: Math.max(2, Math.floor(width / 190)) readonly property real cellWidth: columns > 0 ? (width - (columns - 1) * 10) / columns : 160 Grid { id: grid width: parent.width columns: root.columns spacing: 10 Repeater { model: root.shown Rectangle { id: tile required property var modelData readonly property bool current: Wallpaper.active === tile.modelData width: root.cellWidth height: Math.round(root.cellWidth * 9 / 16) radius: Theme.cardRadius color: Theme.alpha(Theme.bgDark, 0.6) clip: true border.width: 0 Image { id: thumbnail anchors.fill: parent source: "file://" + tile.modelData fillMode: Image.PreserveAspectCrop asynchronous: true cache: false // Decode to roughly the size actually drawn. Without this a // grid of 12MB photographs decodes at full resolution. sourceSize.width: 400 sourceSize.height: 240 // Several of these are 8-12MB originals, so a tile can sit // empty for a second or two. Fading in on ready makes that // read as loading rather than as a broken image, and the // fade runs once per tile rather than continuously. opacity: status === Image.Ready ? 1 : 0 Behavior on opacity { NumberAnimation { duration: Theme.durNormal; easing.type: Easing.OutQuad } } } Text { anchors.centerIn: parent visible: thumbnail.status === Image.Loading text: "Loading…" color: Theme.fgMuted font.family: Theme.fontFamily font.pixelSize: Theme.fontSizeSmall } Text { anchors.centerIn: parent width: parent.width - 20 horizontalAlignment: Text.AlignHCenter wrapMode: Text.WordWrap visible: thumbnail.status === Image.Error text: "Could not read this image" color: Theme.fgMuted font.family: Theme.fontFamily font.pixelSize: Theme.fontSizeSmall } // The prism marks the active wallpaper, the same way it marks // the focused window and the selected sidebar entry. Rectangle { anchors.fill: parent radius: parent.radius visible: tile.current color: "transparent" border.width: 2 border.color: Theme.accent } Rectangle { anchors.left: parent.left anchors.right: parent.right anchors.bottom: parent.bottom height: 26 visible: tile.current || hover.hovered border.width: 0 gradient: Gradient { GradientStop { position: 0.0; color: Theme.alpha(Theme.bgDark, 0.0) } GradientStop { position: 1.0; color: Theme.alpha(Theme.bgDark, 0.88) } } Text { anchors.left: parent.left anchors.right: parent.right anchors.bottom: parent.bottom anchors.margins: 7 text: tile.current ? "Current wallpaper" : Wallpaper.titleFor(tile.modelData) color: tile.current ? Theme.accent : Theme.fg font.family: Theme.fontFamily font.pixelSize: Theme.fontSizeSmall font.weight: tile.current ? Font.DemiBold : Font.Normal elide: Text.ElideRight } } HoverHandler { id: hover } TapHandler { onTapped: Wallpaper.set(tile.modelData) } } } } Text { anchors.centerIn: parent visible: Wallpaper.available.length === 0 && !Wallpaper.scanning width: parent.width - 40 horizontalAlignment: Text.AlignHCenter wrapMode: Text.WordWrap text: "No images found. Looked in ~/Pictures/Wallpapers, ~/Pictures/Backgrounds, ~/.local/share/backgrounds, and /usr/share/backgrounds." color: Theme.fgMuted font.family: Theme.fontFamily font.pixelSize: Theme.fontSizeSmall } }