mirror of
https://github.com/wownero/wowlet.git
synced 2026-10-29 04:08:43 +01:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c7fca31653 | ||
|
|
081c01ada9 | ||
|
|
613d4e7f61 | ||
|
|
79daae6796 | ||
|
|
b1e751a6b7 | ||
|
|
068e4c2aa5 | ||
|
|
f1144c483e | ||
|
|
e5b8ef6a00 |
+1
-1
@@ -1,7 +1,7 @@
|
||||
cmake_minimum_required(VERSION 3.18)
|
||||
|
||||
project(feather
|
||||
VERSION "5.1.1"
|
||||
VERSION "5.1.2"
|
||||
DESCRIPTION "A free Monero desktop wallet"
|
||||
LANGUAGES CXX C ASM
|
||||
)
|
||||
|
||||
+1
-1
@@ -252,7 +252,7 @@ void MainWindow::initWidgets() {
|
||||
});
|
||||
|
||||
// [Mining] — wowlet: the pixel-art mining tab (mining.qml + embedded-node solo miner)
|
||||
m_miningWidget = new MiningWidget(m_wallet, this);
|
||||
m_miningWidget = new MiningWidget(m_wallet, m_nodes, this);
|
||||
ui->tabWidget->addTab(m_miningWidget, "Mining");
|
||||
|
||||
// [Notes]
|
||||
|
||||
@@ -23,7 +23,7 @@ ReceiveWidget::ReceiveWidget(Wallet *wallet, QWidget *parent)
|
||||
|
||||
// wowlet: ambient scene as a short banner under the address table. A layout
|
||||
// item, never an overlay over the table.
|
||||
auto *scene = new SceneWidget(this, SceneWidget::Dawn);
|
||||
auto *scene = new SceneWidget(this, SceneWidget::Dawn, SceneWidget::Sit);
|
||||
scene->setMinimumHeight(110);
|
||||
scene->setMaximumHeight(130);
|
||||
ui->verticalLayout->addWidget(scene);
|
||||
|
||||
+10
-3
@@ -29,10 +29,17 @@ SendWidget::SendWidget(Wallet *wallet, QWidget *parent)
|
||||
// wowlet: the form only needs the top of the tab — give the rest of it to
|
||||
// the ambient scene. A layout item under the form, never an overlay: the
|
||||
// send form itself stays untouched.
|
||||
auto *scene = new SceneWidget(this, SceneWidget::Night);
|
||||
//
|
||||
// Centred in the space below the form rather than stacked straight under
|
||||
// it: butted up against the Clear/Send row the scene reads as part of the
|
||||
// form, and a band of night sky touching a button looks like a mistake.
|
||||
auto *scene = new SceneWidget(this, SceneWidget::Night, SceneWidget::Money);
|
||||
scene->setMinimumHeight(160);
|
||||
scene->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
ui->verticalLayoutOuter->addWidget(scene, 1);
|
||||
scene->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
|
||||
ui->verticalLayoutOuter->addSpacing(14); // never touches the Send row
|
||||
ui->verticalLayoutOuter->addStretch(1);
|
||||
ui->verticalLayoutOuter->addWidget(scene);
|
||||
ui->verticalLayoutOuter->addStretch(1);
|
||||
|
||||
QString amount_rx = R"(^\d{0,8}[\.,]\d{0,12}|(all)$)";
|
||||
QRegularExpression rx;
|
||||
|
||||
@@ -512,6 +512,10 @@ Rectangle {
|
||||
Image {
|
||||
id: imgAxe
|
||||
visible: mining.daemonMiningState === 0
|
||||
// Greyed out while the wallet is on someone else's node:
|
||||
// solo mining is an RPC to the daemon we are connected
|
||||
// to, and that daemon has to be ours.
|
||||
opacity: mining.canMine ? 1.0 : 0.35
|
||||
source: "qrc:/mining/axe.png"
|
||||
width: 73
|
||||
height: 75
|
||||
@@ -551,7 +555,11 @@ Rectangle {
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
|
||||
onClicked: {
|
||||
if(mining.daemonMiningState === 0) {
|
||||
if (mining.daemonMiningState === 0) {
|
||||
if (!mining.canMine) {
|
||||
remoteNodeNotice.flash();
|
||||
return;
|
||||
}
|
||||
root.startMining();
|
||||
root.calcAvailableHeightConsoleLines();
|
||||
}
|
||||
@@ -559,14 +567,56 @@ Rectangle {
|
||||
root.stopMining();
|
||||
}
|
||||
onEntered: {
|
||||
imgAxe.height = 64
|
||||
imgAxe.width = 64
|
||||
if (mining.canMine) {
|
||||
imgAxe.height = 64
|
||||
imgAxe.width = 64
|
||||
} else {
|
||||
remoteNodeNotice.show();
|
||||
}
|
||||
}
|
||||
onExited: {
|
||||
imgAxe.height = 75
|
||||
imgAxe.width = 73
|
||||
remoteNodeNotice.hideSoon();
|
||||
}
|
||||
}
|
||||
|
||||
// Why the axe is greyed out. Shown on hover, and flashed if
|
||||
// the axe is clicked anyway.
|
||||
Rectangle {
|
||||
id: remoteNodeNotice
|
||||
z: parent.z + 5
|
||||
visible: opacity > 0.01
|
||||
opacity: 0.0
|
||||
width: 250
|
||||
height: noticeText.implicitHeight + 16
|
||||
radius: 6
|
||||
color: "#231a10"
|
||||
border.color: "#e0b062"
|
||||
border.width: 1
|
||||
anchors.right: parent.left
|
||||
anchors.rightMargin: 8
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
Text {
|
||||
id: noticeText
|
||||
anchors.centerIn: parent
|
||||
width: parent.width - 16
|
||||
wrapMode: Text.WordWrap
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
color: "#f2dcb4"
|
||||
font.pointSize: 9
|
||||
text: "Mining needs your own node.\nYou are connected to a remote one — " +
|
||||
"start the built-in node or point the wallet at a local daemon " +
|
||||
"under Settings to mine."
|
||||
}
|
||||
|
||||
Behavior on opacity { NumberAnimation { duration: 160 } }
|
||||
Timer { id: noticeHide; interval: 2600; onTriggered: remoteNodeNotice.opacity = 0.0 }
|
||||
function show() { noticeHide.stop(); opacity = 1.0 }
|
||||
function hideSoon() { noticeHide.restart() }
|
||||
function flash() { show(); noticeHide.restart() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+797
-84
File diff suppressed because it is too large
Load Diff
@@ -3,6 +3,8 @@
|
||||
|
||||
#include "MiningWidget.h"
|
||||
|
||||
#include "utils/nodes.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <QVBoxLayout>
|
||||
@@ -20,9 +22,10 @@ static QString formatUptime(qint64 secs) {
|
||||
.arg(secs % 60, 2, 10, QChar('0'));
|
||||
}
|
||||
|
||||
MiningWidget::MiningWidget(Wallet *wallet, QWidget *parent)
|
||||
MiningWidget::MiningWidget(Wallet *wallet, Nodes *nodes, QWidget *parent)
|
||||
: QWidget(parent)
|
||||
, m_wallet(wallet)
|
||||
, m_nodes(nodes)
|
||||
, m_quickWidget(new QQuickWidget(this))
|
||||
, m_backend(new MiningBackend(this))
|
||||
, m_timer(new QTimer(this))
|
||||
@@ -46,6 +49,10 @@ MiningWidget::MiningWidget(Wallet *wallet, QWidget *parent)
|
||||
}
|
||||
|
||||
void MiningWidget::onStartMining() {
|
||||
// The QML disables the control, but the signal is not a promise: refuse
|
||||
// here too rather than send start_mining to a stranger's daemon.
|
||||
if (!m_backend->canMine())
|
||||
return;
|
||||
int threads = std::max(1, QThread::idealThreadCount() / 2);
|
||||
if (m_wallet->startMining(threads))
|
||||
m_backend->setState(3);
|
||||
@@ -57,6 +64,8 @@ void MiningWidget::onStopMining() {
|
||||
}
|
||||
|
||||
void MiningWidget::poll() {
|
||||
m_backend->setCanMine(m_nodes && m_nodes->connection().isLocal());
|
||||
|
||||
bool mining = m_wallet->isMining();
|
||||
m_backend->setState(mining ? 3 : 0);
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#define WOWLET_MININGWIDGET_H
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
class Nodes;
|
||||
#include <QTimer>
|
||||
#include <QQuickWidget>
|
||||
|
||||
@@ -16,16 +18,24 @@ class MiningBackend : public QObject {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(int daemonMiningState READ daemonMiningState NOTIFY daemonMiningStateChanged)
|
||||
Q_PROPERTY(bool reducedMotion READ reducedMotion NOTIFY reducedMotionChanged)
|
||||
Q_PROPERTY(bool canMine READ canMine NOTIFY canMineChanged)
|
||||
public:
|
||||
explicit MiningBackend(QObject *parent = nullptr) : QObject(parent) {}
|
||||
int daemonMiningState() const { return m_state; }
|
||||
void setState(int s) { if (m_state != s) { m_state = s; emit daemonMiningStateChanged(); } }
|
||||
bool reducedMotion() const { return m_reducedMotion; }
|
||||
void setReducedMotion(bool r) { if (m_reducedMotion != r) { m_reducedMotion = r; emit reducedMotionChanged(); } }
|
||||
// Solo mining is a start_mining RPC to whichever daemon the wallet is
|
||||
// connected to. Against a remote node that is either refused outright or —
|
||||
// worse, if the operator left the RPC open — it puts someone else's machine
|
||||
// to work on our address. It is only ever ours to ask of our own node.
|
||||
bool canMine() const { return m_canMine; }
|
||||
void setCanMine(bool c) { if (m_canMine != c) { m_canMine = c; emit canMineChanged(); } }
|
||||
|
||||
signals:
|
||||
void daemonMiningStateChanged();
|
||||
void reducedMotionChanged();
|
||||
void canMineChanged();
|
||||
void daemonOutput(const QString &line);
|
||||
void syncStatus(int from, int to, int pct);
|
||||
void uptimeChanged(const QString &uptime);
|
||||
@@ -34,6 +44,7 @@ signals:
|
||||
private:
|
||||
int m_state = 0; // 0:idle 1:startup 2:syncing 3:mining
|
||||
bool m_reducedMotion = false;
|
||||
bool m_canMine = false;
|
||||
};
|
||||
|
||||
// wowlet: the pixel-art mining tab. Hosts mining.qml and wires its pick-axe to the wallet's solo
|
||||
@@ -41,7 +52,7 @@ private:
|
||||
class MiningWidget : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit MiningWidget(Wallet *wallet, QWidget *parent = nullptr);
|
||||
explicit MiningWidget(Wallet *wallet, Nodes *nodes, QWidget *parent = nullptr);
|
||||
|
||||
private slots:
|
||||
void onStartMining();
|
||||
@@ -50,6 +61,7 @@ private slots:
|
||||
|
||||
private:
|
||||
Wallet *m_wallet;
|
||||
Nodes *m_nodes;
|
||||
QQuickWidget *m_quickWidget;
|
||||
MiningBackend *m_backend;
|
||||
QTimer *m_timer;
|
||||
|
||||
@@ -4,18 +4,44 @@
|
||||
#include "SceneWidget.h"
|
||||
|
||||
#include <QQmlContext>
|
||||
#include <QQuickItem>
|
||||
|
||||
#include "utils/config.h"
|
||||
|
||||
SceneWidget::SceneWidget(QWidget *parent, Variant variant)
|
||||
SceneWidget::SceneWidget(QWidget *parent, Variant variant, Mode mode)
|
||||
: QQuickWidget(parent)
|
||||
, m_backend(new SceneBackend(this))
|
||||
{
|
||||
m_backend->setReducedMotion(conf()->get(Config::reducedMotion).toBool());
|
||||
m_backend->setVariant(static_cast<int>(variant));
|
||||
m_backend->setMode(mode == Sit ? QStringLiteral("sit")
|
||||
: mode == Money ? QStringLiteral("money")
|
||||
: QStringLiteral("run"));
|
||||
this->rootContext()->setContextProperty("sceneCtx", m_backend);
|
||||
this->setResizeMode(QQuickWidget::SizeRootObjectToView);
|
||||
this->setSource(QUrl("qrc:/qml/scene.qml"));
|
||||
this->callScene("suspend");
|
||||
}
|
||||
|
||||
void SceneWidget::showEvent(QShowEvent *event) {
|
||||
QQuickWidget::showEvent(event);
|
||||
this->callScene("restart");
|
||||
}
|
||||
|
||||
void SceneWidget::hideEvent(QHideEvent *event) {
|
||||
this->callScene("suspend");
|
||||
QQuickWidget::hideEvent(event);
|
||||
}
|
||||
|
||||
void SceneWidget::resizeEvent(QResizeEvent *event) {
|
||||
QQuickWidget::resizeEvent(event);
|
||||
if (this->isVisible())
|
||||
this->callScene("restart");
|
||||
}
|
||||
|
||||
void SceneWidget::callScene(const char *method) {
|
||||
if (auto *root = this->rootObject())
|
||||
QMetaObject::invokeMethod(root, method);
|
||||
}
|
||||
|
||||
void SceneWidget::refreshReducedMotion() {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#define WOWLET_SCENEWIDGET_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QQuickWidget>
|
||||
|
||||
// Context object exposed to scene.qml as `sceneCtx`. Carries the reduced-motion
|
||||
@@ -14,18 +15,23 @@ class SceneBackend : public QObject {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool reducedMotion READ reducedMotion NOTIFY reducedMotionChanged)
|
||||
Q_PROPERTY(int variant READ variant NOTIFY variantChanged)
|
||||
Q_PROPERTY(QString mode READ mode NOTIFY modeChanged)
|
||||
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(); } }
|
||||
int variant() const { return m_variant; }
|
||||
void setVariant(int v) { if (m_variant != v) { m_variant = v; emit variantChanged(); } }
|
||||
QString mode() const { return m_mode; }
|
||||
void setMode(const QString &m) { if (m_mode != m) { m_mode = m; emit modeChanged(); } }
|
||||
signals:
|
||||
void reducedMotionChanged();
|
||||
void variantChanged();
|
||||
void modeChanged();
|
||||
private:
|
||||
bool m_reducedMotion = false;
|
||||
int m_variant = 0;
|
||||
QString m_mode = QStringLiteral("run");
|
||||
};
|
||||
|
||||
// wowlet: reusable ambient-scene surface. Hosts scene.qml and reads the
|
||||
@@ -38,9 +44,32 @@ public:
|
||||
// same field. Must match the `skies` table in scene.qml.
|
||||
enum Variant { Dusk = 0, Dawn = 1, Night = 2 };
|
||||
|
||||
explicit SceneWidget(QWidget *parent = nullptr, Variant variant = Dusk);
|
||||
// How much scene a surface wants. History has room for the whole run;
|
||||
// Receive gets a doge sitting in the field; Send gets money blowing past and
|
||||
// nothing else, so the form it sits under is not competing with a dog.
|
||||
enum Mode { Run, Sit, Money };
|
||||
|
||||
explicit SceneWidget(QWidget *parent = nullptr, Variant variant = Dusk,
|
||||
Mode mode = Run);
|
||||
void refreshReducedMotion();
|
||||
|
||||
protected:
|
||||
// A scene on a hidden tab is parked rather than left running: an unshown
|
||||
// QQuickWidget is 0px wide, and a lap run against a 0px field collects every
|
||||
// coin at once and leaves its litter (+1s, barks) to surface on the real lap
|
||||
// when the tab is finally shown. Parking also keeps a background tab from
|
||||
// burning CPU on animation nobody is looking at.
|
||||
void showEvent(QShowEvent *event) override;
|
||||
void hideEvent(QHideEvent *event) override;
|
||||
// Belt and braces: a scene that is on screen and has just been given a real
|
||||
// size is running, whether or not a show event was the thing that got it
|
||||
// there. `restart` only sets a flag, so re-arming an already-running scene
|
||||
// costs nothing and does not interrupt its lap.
|
||||
void resizeEvent(QResizeEvent *event) override;
|
||||
|
||||
private:
|
||||
void callScene(const char *method);
|
||||
|
||||
SceneBackend *m_backend;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user