From 157d6070847d4e1ce76c5d7cf6d118f25ffa13de Mon Sep 17 00:00:00 2001 From: "Danilo M." Date: Fri, 6 Nov 2026 17:57:10 +0100 Subject: wallpaper: one monitor to preview, a line per transition Selecting was unclear with a tile doing both jobs. A mock monitor now plays whatever line is clicked, and a click changes nothing else; the choice is an Active mark per line, and Random's pool a Switch beside it. Each line shows a frame caught halfway through its transition. MockScreen takes children, drawn on its panel. Co-Authored-By: Claude Opus 5.5 --- desktop/modules/appearance/MockScreen.qml | 3 + desktop/modules/appearance/README.md | 6 +- desktop/modules/appearance/TransitionRow.qml | 120 ++++++++++++++++++++++++++ desktop/modules/appearance/TransitionTile.qml | 120 -------------------------- desktop/modules/appearance/TransitionView.qml | 51 +++++++++++ desktop/modules/appearance/TransitionsTab.qml | 99 +++++++++++++++++---- 6 files changed, 261 insertions(+), 138 deletions(-) create mode 100644 desktop/modules/appearance/TransitionRow.qml delete mode 100644 desktop/modules/appearance/TransitionTile.qml create mode 100644 desktop/modules/appearance/TransitionView.qml (limited to 'desktop/modules/appearance') diff --git a/desktop/modules/appearance/MockScreen.qml b/desktop/modules/appearance/MockScreen.qml index 7f6870b..d8e14c9 100644 --- a/desktop/modules/appearance/MockScreen.qml +++ b/desktop/modules/appearance/MockScreen.qml @@ -21,6 +21,8 @@ Item { property string label: "" property string source: "" property bool targeted: false + // Drawn on the panel, over the picture. + default property alias content: panel.data // The panel's size; the bezel and stand are added around it. property real panelWidth: 100 @@ -49,6 +51,7 @@ Item { // Screen. Rectangle { + id: panel anchors.centerIn: parent width: mock.panelWidth height: mock.panelHeight diff --git a/desktop/modules/appearance/README.md b/desktop/modules/appearance/README.md index ab61811..2c9a6ad 100644 --- a/desktop/modules/appearance/README.md +++ b/desktop/modules/appearance/README.md @@ -162,8 +162,10 @@ Apply, and every engine change, plays a transition over the swap, the old picture giving way to the new one (`WallpaperSpread.qml`, `WallShader.qml`, one `.frag` each: burn, ink, shatter, ripple, melt, blinds, crosswarp). The Transitions page picks one, or Random, which draws from the ticked ones and -never repeats the last. Clicking a tile plays it between the two current -wallpapers through the same shader. Both screens play the same one in a run. +never repeats the last. Each line shows a frame halfway through its +transition; clicking one plays it on the page's mock monitor, between the two +current wallpapers, through the same shader, and only the Active mark changes +the choice. Both screens play the same one in a run. Each shader must draw the old picture exactly at `progress` 0 and the new one at 1, or the cover pops as it comes and goes. Pictures reach a shader through a layer, since an `Image` sampled directly gives the whole picture and not its diff --git a/desktop/modules/appearance/TransitionRow.qml b/desktop/modules/appearance/TransitionRow.qml new file mode 100644 index 0000000..a127e0b --- /dev/null +++ b/desktop/modules/appearance/TransitionRow.qml @@ -0,0 +1,120 @@ +// Copyright (C) 2026 Danilo M. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +import QtQuick +import "../.." + +// One line of the transitions page: a frame caught halfway through the +// transition, its name, the Active mark, and the switch for Random's pool. A +// click on the line previews it; only the Active mark changes the choice. +// The line named "random" has no frame to preview and no switch. +Item { + id: row + required property string name + required property string label + // Column widths, shared with the page's headings. + required property int activeWidth + required property int randomWidth + readonly property bool random: name === "random" + readonly property bool active: Transitions.draftPick === name + signal preview + signal activated + + implicitHeight: 76 + + Rectangle { + anchors.fill: parent + radius: 6 + color: row.active ? Qt.alpha(Theme.accent, 0.14) + : area.containsMouse ? Qt.alpha(Theme.text, 0.06) : "transparent" + } + + MouseArea { + id: area + anchors.fill: parent + enabled: !row.random + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: row.preview() + } + + Rectangle { + id: frame + anchors { left: parent.left; leftMargin: 6; verticalCenter: parent.verticalCenter } + // DP-1's shape, like the monitor above. + width: 152; height: 64 + color: Theme.surface + + TransitionView { + anchors.fill: parent + visible: !row.random + name: row.random ? "burn" : row.name + progress: 0.5 + origin: Qt.point(0.35, 0.6) + } + Text { + anchors.centerIn: parent + visible: row.random + // nf-fa-shuffle, present in Inconsolata Nerd Font. + text: "" + color: Theme.subtext + font { family: Theme.iconFamily; pixelSize: Theme.fontSize * 2 } + } + } + + Text { + anchors { left: frame.right; leftMargin: 14; verticalCenter: parent.verticalCenter } + text: row.label + color: row.active ? Theme.text : Theme.subtext + font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 2 } + } + + Item { + id: randomCell + anchors { right: parent.right; top: parent.top; bottom: parent.bottom } + width: row.randomWidth + + Switch { + anchors.centerIn: parent + visible: !row.random + checked: Transitions.draftPool.indexOf(row.name) >= 0 + onToggled: Transitions.toggle(row.name) + } + } + + Item { + anchors { right: randomCell.left; top: parent.top; bottom: parent.bottom } + width: row.activeWidth + + Rectangle { + anchors.centerIn: parent + width: 20; height: 20; radius: 10 + color: "transparent" + border.width: 2 + border.color: row.active ? Theme.accent : Qt.alpha(Theme.text, 0.3) + + Rectangle { + anchors.centerIn: parent + visible: row.active + width: 10; height: 10; radius: 5 + color: Theme.accent + } + } + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + Transitions.draftPick = row.name; + row.activated(); + } + } + } +} diff --git a/desktop/modules/appearance/TransitionTile.qml b/desktop/modules/appearance/TransitionTile.qml deleted file mode 100644 index 9c6d9eb..0000000 --- a/desktop/modules/appearance/TransitionTile.qml +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright (C) 2026 Danilo M. -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License version 2 as -// published by the Free Software Foundation. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -import QtQuick -import "../.." - -// One choice on the transitions page. A click selects it in the draft and -// plays it once between the two current wallpapers. The tick puts it in -// Random's pool. The tile named "random" has no shader and no tick. -Item { - id: tile - required property string name - required property string label - readonly property bool random: name === "random" - readonly property bool selected: Transitions.draftPick === name - readonly property bool shows: !random && Wallpapers.currentH !== "" && Wallpapers.currentV !== "" - property real progress: 0 - property point origin: Qt.point(0.5, 0.5) - - implicitHeight: thumb.height + 30 - - Glow { - anchors.fill: thumb - on: tile.selected - } - - Rectangle { - id: thumb - width: tile.width - height: Math.round(width * 9 / 16) - color: Theme.surface - clip: true - - // Same layering as the cover, WallpaperSpread.qml. ponytail: rests on - // the old picture again after playing, with a cut. - Image { - id: newImg - visible: tile.shows - anchors.fill: parent - source: tile.shows ? Wallpapers.currentV : "" - fillMode: Image.PreserveAspectCrop - sourceSize: Qt.size(thumb.width, thumb.height) - asynchronous: true - layer.enabled: tile.shows - } - Image { - id: oldImg - visible: tile.shows - anchors.fill: parent - source: tile.shows ? Wallpapers.currentH : "" - fillMode: Image.PreserveAspectCrop - sourceSize: Qt.size(thumb.width, thumb.height) - asynchronous: true - layer.enabled: tile.shows - layer.effect: WallShader { - name: tile.name - target: newImg - progress: tile.progress - aspect: thumb.width / Math.max(1, thumb.height) - origin: tile.origin - } - } - - Text { - anchors.centerIn: parent - visible: tile.random - // nf-fa-shuffle, present in Inconsolata Nerd Font. - text: "" - color: Theme.subtext - font { family: Theme.iconFamily; pixelSize: Theme.fontSize * 2 } - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Transitions.draftPick = tile.name; - if (!tile.shows) return; - tile.origin = Qt.point(Math.random(), Math.random()); - play.restart(); - } - } - } - - NumberAnimation { - id: play - target: tile - property: "progress" - from: 0 - to: 1 - duration: Transitions.duration - onFinished: tile.progress = 0 - } - - Toggle { - anchors { top: thumb.bottom; topMargin: 6; left: parent.left } - visible: !tile.random - label: tile.label - // Toggle assigns checked itself, which would break a plain binding - // and leave the tick stale after Reset. - Binding on checked { value: Transitions.draftPool.indexOf(tile.name) >= 0 } - onToggled: Transitions.toggle(tile.name) - } - - Text { - anchors { top: thumb.bottom; topMargin: 8; left: parent.left } - visible: tile.random - text: tile.label - color: Theme.subtext - font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 4 } - } -} diff --git a/desktop/modules/appearance/TransitionView.qml b/desktop/modules/appearance/TransitionView.qml new file mode 100644 index 0000000..44511a8 --- /dev/null +++ b/desktop/modules/appearance/TransitionView.qml @@ -0,0 +1,51 @@ +// Copyright (C) 2026 Danilo M. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +import QtQuick +import "../.." + +// The two current wallpapers with a transition at `progress`, laid out as the +// cover lays them out in WallpaperSpread.qml, for the transitions page. Draws +// nothing while either wallpaper is unknown. +Item { + id: view + property string name: "burn" + property real progress: 0 + property point origin: Qt.point(0.5, 0.5) + readonly property bool shows: Wallpapers.currentH !== "" && Wallpapers.currentV !== "" + + Image { + id: newImg + visible: view.shows + anchors.fill: parent + source: view.shows ? "file://" + Wallpapers.currentV : "" + fillMode: Image.PreserveAspectCrop + sourceSize: Qt.size(view.width, view.height) + asynchronous: true + layer.enabled: view.shows + } + Image { + visible: view.shows + anchors.fill: parent + source: view.shows ? "file://" + Wallpapers.currentH : "" + fillMode: Image.PreserveAspectCrop + sourceSize: Qt.size(view.width, view.height) + asynchronous: true + layer.enabled: view.shows + layer.effect: WallShader { + name: view.name + target: newImg + progress: view.progress + aspect: view.width / Math.max(1, view.height) + origin: view.origin + } + } +} diff --git a/desktop/modules/appearance/TransitionsTab.qml b/desktop/modules/appearance/TransitionsTab.qml index 190a117..5da88f2 100644 --- a/desktop/modules/appearance/TransitionsTab.qml +++ b/desktop/modules/appearance/TransitionsTab.qml @@ -16,42 +16,109 @@ Item { id: tab implicitHeight: col.implicitHeight - readonly property int gap: 14 - readonly property int columns: Math.max(1, Math.floor((width + gap) / 260)) - readonly property real tileWidth: (width - (columns - 1) * gap) / columns + readonly property int activeWidth: 70 + readonly property int randomWidth: 90 + + // What the monitor shows: the last one previewed, or else the choice. + property string previewed: "" + readonly property string staged: previewed !== "" ? previewed + : Transitions.draftPick !== "random" ? Transitions.draftPick + : Transitions.draftPool[0] ?? "burn" + property real progress: 0 + property point origin: Qt.point(0.5, 0.5) + + function play(name) { + previewed = name; + origin = Qt.point(Math.random(), Math.random()); + anim.restart(); + } + + NumberAnimation { + id: anim + target: tab + property: "progress" + from: 0 + to: 1 + duration: Transitions.duration + // ponytail: back on the old picture with a cut. + onFinished: tab.progress = 0 + } Column { id: col width: parent.width spacing: 14 + MockScreen { + anchors.horizontalCenter: parent.horizontalCenter + panelWidth: Math.min(col.width, 640) + panelHeight: Math.round(panelWidth * 1080 / 2560) + + TransitionView { + anchors.fill: parent + name: tab.staged + progress: tab.progress + origin: tab.origin + } + } + Text { width: col.width + horizontalAlignment: Text.AlignHCenter wrapMode: Text.Wrap - text: "Click a transition to see it between your two current wallpapers. " - + "Random draws from the ticked ones, and never the same one twice running." + text: (Transitions.all.find(t => t.name === tab.staged) ?? { label: "" }).label + + ". Click a line to play it here; Active is what wallpaper changes use." color: Theme.overlay font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 4 } } - Grid { - columns: tab.columns - spacing: tab.gap + Column { + width: col.width + spacing: 2 + + Item { + width: parent.width + height: 22 + + Text { + anchors { left: parent.left; leftMargin: 6; verticalCenter: parent.verticalCenter } + text: "Transition" + color: Theme.overlay + font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 4 } + } + Text { + id: randomHead + anchors { right: parent.right; verticalCenter: parent.verticalCenter } + width: tab.randomWidth + horizontalAlignment: Text.AlignHCenter + text: "In Random" + color: Theme.overlay + font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 4 } + } + Text { + anchors { right: randomHead.left; verticalCenter: parent.verticalCenter } + width: tab.activeWidth + horizontalAlignment: Text.AlignHCenter + text: "Active" + color: Theme.overlay + font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 4 } + } + } Repeater { - model: Transitions.all - TransitionTile { + model: Transitions.all.concat([{ name: "random", label: "Random" }]) + + TransitionRow { required property var modelData - width: tab.tileWidth + width: parent.width name: modelData.name label: modelData.label + activeWidth: tab.activeWidth + randomWidth: tab.randomWidth + onPreview: tab.play(name) + onActivated: if (!random) tab.play(name) } } - TransitionTile { - width: tab.tileWidth - name: "random" - label: "Random" - } } } } -- cgit v1.2.3