diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d58dab71..0db6c33b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -34,7 +34,7 @@ if (CHECK_UPDATES) add_subdirectory(openpgp) endif() -qt_add_resources(RESOURCES assets.qrc assets_tor.qrc assets_wownerod.qrc assets_docs.qrc mining.qrc silly.qrc) +qt_add_resources(RESOURCES assets.qrc assets_tor.qrc assets_wownerod.qrc assets_docs.qrc mining.qrc silly.qrc scene.qrc) # Compile source files (.h/.cpp) file(GLOB SOURCE_FILES diff --git a/src/assets/images/scene/dog.gif b/src/assets/images/scene/dog.gif new file mode 100644 index 00000000..433ee586 Binary files /dev/null and b/src/assets/images/scene/dog.gif differ diff --git a/src/assets/images/scene/dog2.gif b/src/assets/images/scene/dog2.gif new file mode 100644 index 00000000..f773aeed Binary files /dev/null and b/src/assets/images/scene/dog2.gif differ diff --git a/src/assets/images/scene/flyingmoney.gif b/src/assets/images/scene/flyingmoney.gif new file mode 100644 index 00000000..7ff0bf4c Binary files /dev/null and b/src/assets/images/scene/flyingmoney.gif differ diff --git a/src/assets/images/scene/goldcoin.gif b/src/assets/images/scene/goldcoin.gif new file mode 100644 index 00000000..010c087c Binary files /dev/null and b/src/assets/images/scene/goldcoin.gif differ diff --git a/src/assets/images/scene/grass.png b/src/assets/images/scene/grass.png new file mode 100644 index 00000000..d3f74dd2 Binary files /dev/null and b/src/assets/images/scene/grass.png differ diff --git a/src/assets/qml/scene.qml b/src/assets/qml/scene.qml new file mode 100644 index 00000000..2edc2f3b --- /dev/null +++ b/src/assets/qml/scene.qml @@ -0,0 +1,49 @@ +import QtQuick + +// wowlet ambient scene — dusk sky, tiled grass, a running doge, bouncing WOW +// coins, drifting money. All motion gates on `scene.reducedMotion` (exposed by +// the host SceneBackend); frozen it is the reduced-motion still. +Rectangle { + id: scene + property bool reducedMotion: (typeof sceneCtx !== 'undefined' && sceneCtx) ? sceneCtx.reducedMotion : false + + gradient: Gradient { + GradientStop { position: 0.0; color: "#160d2b" } + GradientStop { position: 0.5; color: "#5b2f6e" } + GradientStop { position: 0.82; color: "#c96aa0" } + GradientStop { position: 1.0; color: "#f2adc9" } + } + + Rectangle { x: parent.width*0.74; y: parent.height*0.14; width: 62; height: 62; radius: 31; color: "#ffe9b0"; opacity: 0.9 } + + AnimatedImage { + source: "qrc:/scene/flyingmoney.gif"; playing: !scene.reducedMotion + width: 70; height: 64; y: 30; x: 120; opacity: 0.9 + NumberAnimation on x { running: !scene.reducedMotion; from: -80; to: scene.width; duration: 11000; loops: Animation.Infinite } + } + + Row { + anchors.bottom: parent.bottom + Repeater { model: Math.ceil(scene.width/180)+1 + Image { source: "qrc:/scene/grass.png"; width: 180; height: 46; smooth: false } } + } + + Repeater { + model: 3 + AnimatedImage { + source: "qrc:/scene/goldcoin.gif"; playing: !scene.reducedMotion + width: 34; height: 34; x: 150 + index*170; y: scene.height*0.46 + SequentialAnimation on y { + running: !scene.reducedMotion; loops: Animation.Infinite + NumberAnimation { to: scene.height*0.36; duration: 1050 + index*170; easing.type: Easing.OutQuad } + NumberAnimation { to: scene.height*0.5; duration: 1050 + index*170; easing.type: Easing.InQuad } + } + } + } + + AnimatedImage { + source: "qrc:/scene/dog.gif"; playing: !scene.reducedMotion + width: 104; height: 83; y: scene.height - 46 - 62; x: 70 + NumberAnimation on x { running: !scene.reducedMotion; from: -110; to: scene.width; duration: 6500; loops: Animation.Infinite } + } +} diff --git a/src/scene.qrc b/src/scene.qrc new file mode 100644 index 00000000..3e864792 --- /dev/null +++ b/src/scene.qrc @@ -0,0 +1,10 @@ + + + assets/qml/scene.qml + assets/images/scene/dog2.gif + assets/images/scene/dog.gif + assets/images/scene/flyingmoney.gif + assets/images/scene/goldcoin.gif + assets/images/scene/grass.png + + diff --git a/src/widgets/SceneWidget.cpp b/src/widgets/SceneWidget.cpp new file mode 100644 index 00000000..32c7c98c --- /dev/null +++ b/src/widgets/SceneWidget.cpp @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: BSD-3-Clause +// SPDX-FileCopyrightText: The Monero Project + +#include "SceneWidget.h" + +#include + +#include "utils/config.h" + +SceneWidget::SceneWidget(QWidget *parent) + : QQuickWidget(parent) + , m_backend(new SceneBackend(this)) +{ + m_backend->setReducedMotion(conf()->get(Config::reducedMotion).toBool()); + this->rootContext()->setContextProperty("sceneCtx", m_backend); + this->setResizeMode(QQuickWidget::SizeRootObjectToView); + this->setSource(QUrl("qrc:/qml/scene.qml")); +} + +void SceneWidget::refreshReducedMotion() { + m_backend->setReducedMotion(conf()->get(Config::reducedMotion).toBool()); +} diff --git a/src/widgets/SceneWidget.h b/src/widgets/SceneWidget.h new file mode 100644 index 00000000..635be1b9 --- /dev/null +++ b/src/widgets/SceneWidget.h @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: BSD-3-Clause +// SPDX-FileCopyrightText: The Monero Project + +#ifndef WOWLET_SCENEWIDGET_H +#define WOWLET_SCENEWIDGET_H + +#include +#include + +// Context object exposed to scene.qml as `sceneCtx`. Carries the reduced-motion +// setting so the scene freezes into its still (reduced-motion) form. +class SceneBackend : public QObject { +Q_OBJECT + Q_PROPERTY(bool reducedMotion READ reducedMotion NOTIFY reducedMotionChanged) +public: + explicit SceneBackend(QObject *parent = nullptr) : QObject(parent) {} + bool reducedMotion() const { return m_reducedMotion; } + void setReducedMotion(bool r) { if (m_reducedMotion != r) { m_reducedMotion = r; emit reducedMotionChanged(); } } +signals: + void reducedMotionChanged(); +private: + bool m_reducedMotion = false; +}; + +// wowlet: reusable ambient-scene surface. Hosts scene.qml and reads the +// reduced-motion preference from Config. Drop it behind a form, into a banner, +// or over the window as an idle screensaver. +class SceneWidget : public QQuickWidget { +Q_OBJECT +public: + explicit SceneWidget(QWidget *parent = nullptr); + void refreshReducedMotion(); +private: + SceneBackend *m_backend; +}; + +#endif //WOWLET_SCENEWIDGET_H diff --git a/src/widgets/WalletUnlockWidget.cpp b/src/widgets/WalletUnlockWidget.cpp index 1c29fe08..6157113f 100644 --- a/src/widgets/WalletUnlockWidget.cpp +++ b/src/widgets/WalletUnlockWidget.cpp @@ -9,6 +9,7 @@ #include "utils/Utils.h" #include "libwalletqt/Wallet.h" +#include "widgets/SceneWidget.h" WalletUnlockWidget::WalletUnlockWidget(QWidget *parent, Wallet *wallet) : QWidget(parent) @@ -18,6 +19,13 @@ WalletUnlockWidget::WalletUnlockWidget(QWidget *parent, Wallet *wallet) ui->setupUi(this); this->reset(); + // wowlet: ambient scene banner above the unlock form (freezes itself into a + // still when "Prefer reduced motion" is set). + auto *scene = new SceneWidget(this); + scene->setMinimumHeight(120); + scene->setMaximumHeight(140); + ui->verticalLayout->insertWidget(0, scene); + ui->buttonBox->button(QDialogButtonBox::Ok)->setAutoDefault(true); connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &WalletUnlockWidget::tryUnlock);