Watch
1
0
Fork
You've already forked souveraine
0

phone: wallpaper picker settings page, drag-follow PIN reveal

Wallpaper grid in the settings app applying through the shell's
wallpapers IPC. Lock pad now tracks the finger during the swipe and
settles on release instead of snapping at a threshold.
This commit is contained in:
Fimeg 2026-07-19 22:46:01 -04:00
commit 47858d5b06
4 changed files with 139 additions and 8 deletions

View file

@ -53,6 +53,7 @@ modules/common/widgets/ContentPage.qml souveraine/modules/common/widgets/Conte
modules/common/widgets/StyledToolTip.qml souveraine/modules/common/widgets/StyledToolTip.qml
modules/settings/DeviceConfig.qml souveraine/modules/settings/DeviceConfig.qml
modules/settings/LockConfig.qml souveraine/modules/settings/LockConfig.qml
modules/settings/WallpaperConfig.qml souveraine/modules/settings/WallpaperConfig.qml
modules/settings/DockConfig.qml souveraine/modules/settings/DockConfig.qml
modules/settings/NavigationConfig.qml souveraine/modules/settings/NavigationConfig.qml
modules/settings/KeyboardConfig.qml souveraine/modules/settings/KeyboardConfig.qml

View file

@ -36,6 +36,12 @@ MouseArea {
// is untouched; this is purely which stage is presented.
property bool pinRevealed: false
property real pressY: 0
// Live drag: the pad follows the finger during the swipe instead of
// snapping at a threshold. dragOffset is px of upward travel; release
// past commitDistance commits the reveal, anything less springs back.
property bool dragging: false
property real dragOffset: 0
readonly property real commitDistance: 120
function forceFieldFocus() {
root.forceActiveFocus();
@ -55,12 +61,29 @@ MouseArea {
forceFieldFocus();
}
onPositionChanged: mouse => {
if (!root.pinRevealed && root.pressY - mouse.y > 60)
root.pinRevealed = true;
else if (root.pinRevealed && mouse.y - root.pressY > 80
if (!root.pinRevealed) {
const d = root.pressY - mouse.y;
if (d > 8) {
root.dragging = true;
root.dragOffset = Math.max(0, d);
}
} else if (mouse.y - root.pressY > 80
&& root.context.currentText.length === 0
&& !root.context.unlockInProgress)
&& !root.context.unlockInProgress) {
root.pinRevealed = false;
}
}
onReleased: {
// Order matters: dragging must drop first so the settle (up on
// commit, back down on abort) animates from the finger's position.
const commit = !root.pinRevealed && root.dragOffset > root.commitDistance;
root.dragging = false;
if (commit) root.pinRevealed = true;
root.dragOffset = 0;
}
onCanceled: {
root.dragging = false;
root.dragOffset = 0;
}
// Retreat to glance when the pad sits unused and empty.
@ -118,8 +141,10 @@ MouseArea {
z: 2
spacing: 2
visible: opacity > 0
opacity: root.pinRevealed ? 0 : 1
opacity: root.pinRevealed ? 0
: 1 - Math.min(1, root.dragOffset / root.commitDistance)
Behavior on opacity {
enabled: !root.dragging
NumberAnimation { duration: 180 }
}
@ -151,14 +176,19 @@ MouseArea {
z: 2
spacing: 20
visible: opacity > 0
opacity: root.pinRevealed ? 1 : 0
opacity: root.pinRevealed ? 1
: Math.min(1, root.dragOffset / root.commitDistance)
Behavior on opacity {
enabled: !root.dragging
NumberAnimation { duration: 200 }
}
// Slide up from below the screen edge as it reveals.
// Slides with the finger during the drag; on release the Behavior
// takes over and settles it (fully up on commit, back down on abort).
transform: Translate {
y: root.pinRevealed ? 0 : padColumn.height + 48
y: root.pinRevealed ? 0
: Math.max(0, (padColumn.height + 48) - root.dragOffset)
Behavior on y {
enabled: !root.dragging
NumberAnimation {
duration: 260
easing.type: Easing.OutCubic

View file

@ -0,0 +1,95 @@
import QtQuick
import QtQuick.Layouts
import Qt.labs.folderlistmodel
import Quickshell
import qs.services
import qs.modules.common
import qs.modules.common.widgets
// Wallpaper pick from ~/Pictures/Wallpapers inside the settings app.
// Applying goes through the main shell's `wallpapers apply` IPC so the
// selection, config persistence, and matugen theming all run in the shell
// process that owns them; this page is only a view.
ContentPage {
forceWidth: true
readonly property string wallpaperDir: Quickshell.env("HOME") + "/Pictures/Wallpapers"
ContentSection {
icon: "wallpaper"
title: Translation.tr("Wallpaper")
StyledText {
Layout.fillWidth: true
text: Translation.tr("Current: %1").arg(
(Config.options.background.wallpaperPath || Translation.tr("none")).split("/").pop())
color: Appearance.colors.colSubtext
font.pixelSize: Appearance.font.pixelSize.smaller
elide: Text.ElideMiddle
}
GridView {
id: grid
Layout.fillWidth: true
// Rows of ~3 across the phone width; height fits the model.
readonly property int cols: 3
cellWidth: Math.floor(width / cols)
cellHeight: Math.floor(cellWidth * 2) // portrait-ish tiles
implicitHeight: Math.ceil(folderModel.count / cols) * cellHeight
interactive: false // the page scrolls, not the grid
clip: true
model: FolderListModel {
id: folderModel
folder: "file://" + wallpaperDir
nameFilters: ["*.jpg", "*.jpeg", "*.png", "*.webp"]
showDirs: false
}
delegate: Item {
required property string filePath
required property string fileName
width: grid.cellWidth
height: grid.cellHeight
Rectangle {
anchors.fill: parent
anchors.margins: 5
radius: Appearance.rounding.small
color: Appearance.colors.colLayer1
border.width: Config.options.background.wallpaperPath === filePath ? 3 : 0
border.color: Appearance.colors.colPrimary
clip: true
Image {
anchors.fill: parent
anchors.margins: 3
source: "file://" + filePath
fillMode: Image.PreserveAspectCrop
asynchronous: true
sourceSize.width: 240
}
MouseArea {
anchors.fill: parent
onClicked: {
// Shell process owns selection + theming.
Quickshell.execDetached(["qs", "-c", "souveraine",
"ipc", "call", "wallpapers", "apply", filePath]);
Config.options.background.wallpaperPath = filePath;
}
}
}
}
}
StyledText {
visible: folderModel.count === 0
Layout.fillWidth: true
text: Translation.tr("No images in %1").arg(wallpaperDir)
color: Appearance.colors.colSubtext
wrapMode: Text.WordWrap
}
}
}

View file

@ -43,6 +43,11 @@ ApplicationWindow {
icon: "lock",
component: "modules/settings/LockConfig.qml"
},
{
name: Translation.tr("Wallpaper"),
icon: "wallpaper",
component: "modules/settings/WallpaperConfig.qml"
},
{
name: Translation.tr("Dock"),
icon: "dock_to_bottom",