Skip to content

Commit

Permalink
Implement GUI overview screen value masking
Browse files Browse the repository at this point in the history
  • Loading branch information
jamescowens committed Dec 1, 2021
1 parent fbcb66d commit e952ab8
Show file tree
Hide file tree
Showing 9 changed files with 150 additions and 20 deletions.
35 changes: 35 additions & 0 deletions src/qt/bitcoingui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,11 @@ void BitcoinGUI::createActions()
resetblockchainAction = new QAction(tr("&Reset blockchain data"), this);
resetblockchainAction->setToolTip(tr("Remove blockchain data and start chain from zero"));

m_mask_values_action = new QAction(tr("&Mask values"), this);
m_mask_values_action->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_M));
m_mask_values_action->setStatusTip(tr("Mask the values in the Overview screen"));
m_mask_values_action->setCheckable(true);

connect(quitAction, &QAction::triggered, this, &BitcoinGUI::tryQuit);
connect(aboutAction, &QAction::triggered, this, &BitcoinGUI::aboutClicked);
connect(optionsAction, &QAction::triggered, this, &BitcoinGUI::optionsClicked);
Expand Down Expand Up @@ -554,6 +559,8 @@ void BitcoinGUI::createMenuBar()
settings->addSeparator();
settings->addAction(optionsAction);
settings->addAction(openConfigAction);
settings->addSeparator();
settings->addAction(m_mask_values_action);

QMenu *community = appMenuBar->addMenu(tr("&Community"));
community->addAction(bxAction);
Expand Down Expand Up @@ -758,6 +765,13 @@ void BitcoinGUI::setClientModel(ClientModel *clientModel)
// Report errors from network/worker thread
connect(clientModel, &ClientModel::error, this, &BitcoinGUI::error);

// Ensure the checkbox for mask values action matches the retrieved state from the optionsModel.
m_mask_values_action->setChecked(isPrivacyModeActivated());

// Connect the action to the setPrivacy function. (This has to be done after the setting of the
// checkbox state instead of in the createActions above.
connect(m_mask_values_action, &QAction::toggled, this, &BitcoinGUI::setPrivacy);

rpcConsole->setClientModel(clientModel);
addressBookPage->setOptionsModel(clientModel->getOptionsModel());
receiveCoinsPage->setOptionsModel(clientModel->getOptionsModel());
Expand Down Expand Up @@ -839,6 +853,13 @@ void BitcoinGUI::createTrayIcon()
notificator = new Notificator(qApp->applicationName(), trayIcon, this);
}

bool BitcoinGUI::isPrivacyModeActivated() const
{
if (!clientModel || !clientModel->getOptionsModel()) return false;

return clientModel->getOptionsModel()->getMaskValues();
}

void BitcoinGUI::createTrayIconMenu()
{
#ifndef Q_OS_MAC
Expand Down Expand Up @@ -1278,6 +1299,20 @@ void BitcoinGUI::resetblockchainClicked()
}
}

void BitcoinGUI::setPrivacy()
{
if (!clientModel || !clientModel->getOptionsModel()) return;

bool privacy_mode(!clientModel->getOptionsModel()->getMaskValues());

clientModel->getOptionsModel()->setMaskValues(privacy_mode);

// Need to call updateMinerStatus from here to feed back in the Coin Weight to the overview screen.
// Not ideal, but the normal trigger to update the Staking fields on the overview screen normally come from
// the core, not the GUI. Here the privacy state change is coming from the GUI.
clientModel->updateMinerStatus(g_miner_status.StakingActive(), g_miner_status.GetSearchReport().CoinWeight());
}

bool BitcoinGUI::tryQuit()
{
if(clientModel &&
Expand Down
8 changes: 8 additions & 0 deletions src/qt/bitcoingui.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@ class BitcoinGUI : public QMainWindow
*/
void setVotingModel(VotingModel *votingModel);

/**
* @brief Queries the state of privacy mode (mask values on overview screen).
* @return boolean of the mask values state
*/
bool isPrivacyModeActivated() const;

protected:
void changeEvent(QEvent *e);
void closeEvent(QCloseEvent *event);
Expand Down Expand Up @@ -140,6 +146,7 @@ class BitcoinGUI : public QMainWindow
QAction *openRPCConsoleAction;
QAction *snapshotAction;
QAction *resetblockchainAction;
QAction *m_mask_values_action;

QSystemTrayIcon *trayIcon;
QMenu *trayIconMenu;
Expand Down Expand Up @@ -244,6 +251,7 @@ private slots:
void peersClicked();
void snapshotClicked();
void resetblockchainClicked();
void setPrivacy();
bool tryQuit();

#ifndef Q_OS_MAC
Expand Down
11 changes: 9 additions & 2 deletions src/qt/bitcoinunits.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,16 @@ QString BitcoinUnits::formatWithPrivacy(int unit, qint64 amount, bool privacy)

QString BitcoinUnits::formatOverviewRounded(qint64 amount, bool privacy)
{
QString value;

if (amount < factor(BTC)) {
return format(BTC, amount);
if (privacy) {
value = format(BTC, 0, false, false).replace('0', '#');
} else {
value = format(BTC, amount, false, false);
}

return value;
}

qint64 round_scale = 10;
Expand All @@ -154,7 +162,6 @@ QString BitcoinUnits::formatOverviewRounded(qint64 amount, bool privacy)
// Rounds half-down to avoid over-representing the amount:
const qint64 rounded_amount = static_cast<double>(amount) / round_scale;

QString value;
if (privacy) {
value = format(BTC, 0, false, false).replace('0', '#');
} else {
Expand Down
5 changes: 5 additions & 0 deletions src/qt/noresult.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,8 @@ void NoResult::showDefaultLoadingTitle()
{
setTitle(tr("Loading..."));
}

void NoResult::showPrivacyEnabledTitle()
{
setTitle(tr("Privacy Enabled..."));
}
1 change: 1 addition & 0 deletions src/qt/noresult.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public slots:
void showDefaultNothingHereTitle();
void showDefaultNoResultTitle();
void showDefaultLoadingTitle();
void showPrivacyEnabledTitle();

private:
Ui::NoResult *ui;
Expand Down
81 changes: 67 additions & 14 deletions src/qt/overviewpage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -196,8 +196,16 @@ int OverviewPage::getNumTransactionsForView()
// Compute the maximum number of transactions the transaction list widget
// can hold without overflowing.
const size_t itemHeight = txdelegate->height() + ui->listTransactions->spacing();
const size_t contentsHeight = ui->listTransactions->height();
const int numItems = contentsHeight / itemHeight;

// We have to use the frame here because when the listTransactions is hidden, the recentTransactionsNoResult
// takes up the space and would cause the calculation to be off.
const size_t contentsHeight = std::max(ui->recentTransactionsFrame->height() - ui->recentTransLabel->height(), 0);

LogPrint(BCLog::LogFlags::QT, "INFO: %s: contentsHeight = %u, itemHeight = %u",
__func__, contentsHeight, itemHeight);

// take one off so that there is not a "half-visible one" there, ensure not below 0.
const int numItems = std::max((int) (contentsHeight / itemHeight) - 1, 0);

return numItems;
}
Expand All @@ -213,13 +221,13 @@ void OverviewPage::updateTransactions()
// for the "nothing here yet" placeholder in the transaction list. It
// will never appear again:
//
if (numItems > 0)
if (!filter->rowCount())
{
delete ui->recentTransactionsNoResult;
ui->recentTransactionsNoResult = nullptr;
ui->recentTransactionsNoResult->setVisible(true);
}

LogPrint(BCLog::LogFlags::QT, "OverviewPage::updateTransactions(): numItems = %d, getLimit() = %d", numItems, filter->getLimit());
LogPrint(BCLog::LogFlags::QT, "OverviewPage::updateTransactions(): numItems = %d, getLimit() = %d",
numItems, filter->getLimit());

// This is a "stairstep" approach, using x3 to x6 factors to size the setLimit.
// Based on testing with a wallet with a large number of transactions (40k+)
Expand Down Expand Up @@ -262,12 +270,13 @@ void OverviewPage::setBalance(qint64 balance, qint64 stake, qint64 unconfirmedBa
currentStake = stake;
currentUnconfirmedBalance = unconfirmedBalance;
currentImmatureBalance = immatureBalance;
ui->headerBalanceLabel->setText(BitcoinUnits::formatOverviewRounded(balance));
ui->balanceLabel->setText(BitcoinUnits::formatWithUnit(unit, balance));
ui->stakeLabel->setText(BitcoinUnits::formatWithUnit(unit, stake));
ui->unconfirmedLabel->setText(BitcoinUnits::formatWithUnit(unit, unconfirmedBalance));
ui->immatureLabel->setText(BitcoinUnits::formatWithUnit(unit, immatureBalance));
ui->totalLabel->setText(BitcoinUnits::formatWithUnit(unit, balance + stake + unconfirmedBalance + immatureBalance));
ui->headerBalanceLabel->setText(BitcoinUnits::formatOverviewRounded(balance, m_privacy));
ui->balanceLabel->setText(BitcoinUnits::formatWithPrivacy(unit, balance, m_privacy));
ui->stakeLabel->setText(BitcoinUnits::formatWithPrivacy(unit, stake, m_privacy));
ui->unconfirmedLabel->setText(BitcoinUnits::formatWithPrivacy(unit, unconfirmedBalance, m_privacy));
ui->immatureLabel->setText(BitcoinUnits::formatWithPrivacy(unit, immatureBalance, m_privacy));
ui->totalLabel->setText(BitcoinUnits::formatWithPrivacy(unit, balance + stake + unconfirmedBalance + immatureBalance,
m_privacy));

// only show immature (newly mined) balance if it's non-zero, so as not to complicate things
// for the non-mining users
Expand All @@ -289,14 +298,46 @@ void OverviewPage::setDifficulty(double difficulty, double net_weight)

void OverviewPage::setCoinWeight(double coin_weight)
{
ui->coinWeightLabel->setText(QString::number(coin_weight, 'f', 2));
QString text;

if (m_privacy) {
text = QString("#.##");
} else {
text = QString::number(coin_weight, 'f', 2);
}

ui->coinWeightLabel->setText(text);
}

void OverviewPage::setCurrentPollTitle(const QString& title)
{
ui->currentPollsTitleLabel->setText(title);
}

void OverviewPage::setPrivacy(bool privacy)
{
m_privacy = privacy;
int transaction_count = filter->rowCount();

if (currentBalance != -1) {
setBalance(currentBalance, currentStake, currentUnconfirmedBalance, currentImmatureBalance);
}

if (m_privacy) {
ui->recentTransactionsNoResult->showPrivacyEnabledTitle();
} else {
ui->recentTransactionsNoResult->showDefaultNothingHereTitle();
}
ui->recentTransactionsNoResult->setVisible(m_privacy || !transaction_count);
ui->listTransactions->setVisible(!m_privacy && transaction_count);
if (researcherModel) researcherModel->setMaskAccrualAndMagnitude(m_privacy);

LogPrint(BCLog::LogFlags::QT, "INFO: %s: m_privacy = %u", __func__, m_privacy);

updateTransactions();
updatePendingAccrual();
}

void OverviewPage::setResearcherModel(ResearcherModel *researcherModel)
{
this->researcherModel = researcherModel;
Expand Down Expand Up @@ -324,7 +365,13 @@ void OverviewPage::setWalletModel(WalletModel *model)
filter->setDynamicSortFilter(true);
filter->setSortRole(Qt::EditRole);
filter->setShowInactive(false);
filter->setLimit(getNumTransactionsForView());

int num_transactions_for_view = getNumTransactionsForView();
filter->setLimit(num_transactions_for_view);

LogPrint(BCLog::LogFlags::QT, "INFO: %s: num_transactions_for_view = %i, getLimit() = %i",
__func__, num_transactions_for_view, filter->getLimit());

filter->sort(TransactionTableModel::Status, Qt::DescendingOrder);
ui->listTransactions->setModel(filter.get());
ui->listTransactions->setModelColumn(TransactionTableModel::ToAddress);
Expand All @@ -337,6 +384,12 @@ void OverviewPage::setWalletModel(WalletModel *model)

connect(model->getOptionsModel(), &OptionsModel::LimitTxnDisplayChanged, this, &OverviewPage::updateTransactions);
connect(model, &WalletModel::transactionUpdated, this, &OverviewPage::updateTransactions);

// Set the privacy state for the overview screen from the optionsModel for init.
setPrivacy(model->getOptionsModel()->getMaskValues());

// Connect the privacy mode setting to the options dialog.
connect(walletModel->getOptionsModel(), &OptionsModel::MaskValuesChanged, this, & OverviewPage::setPrivacy);
}

// update the display unit, to not use the default ("BTC")
Expand Down
2 changes: 2 additions & 0 deletions src/qt/overviewpage.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public slots:
void setDifficulty(double difficulty, double net_weight);
void setCoinWeight(double coin_weight);
void setCurrentPollTitle(const QString& title);
void setPrivacy(bool privacy);

signals:
void transactionClicked(const QModelIndex &index);
Expand All @@ -55,6 +56,7 @@ public slots:
qint64 currentUnconfirmedBalance;
qint64 currentImmatureBalance;
int scaledDecorationSize;
bool m_privacy = false;

TxViewDelegate *txdelegate;
std::unique_ptr<TransactionFilterProxy> filter;
Expand Down
25 changes: 21 additions & 4 deletions src/qt/researcher/researchermodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,13 @@ void ResearcherModel::setTheme(const QString& theme_name)
emit beaconChanged();
}

void ResearcherModel::setMaskAccrualAndMagnitude(bool privacy)
{
m_privacy_enabled = privacy;

refresh();
}

bool ResearcherModel::configuredForInvestorMode() const
{
return m_configured_for_investor_mode;
Expand Down Expand Up @@ -294,20 +301,30 @@ QString ResearcherModel::formatCpid() const

QString ResearcherModel::formatMagnitude() const
{
QString text;

if (outOfSync()) {
return "...";
text = "...";
} else if (m_privacy_enabled){
text = "#";
} else {
text = QString::fromStdString(m_researcher->Magnitude().ToString());
}

return QString::fromStdString(m_researcher->Magnitude().ToString());
return text;
}

QString ResearcherModel::formatAccrual(const int display_unit) const
{
QString text;

if (outOfSync()) {
return "...";
text = "...";
} else {
text = BitcoinUnits::formatWithPrivacy(display_unit, m_researcher->Accrual(), m_privacy_enabled);
}

return BitcoinUnits::formatWithUnit(display_unit, m_researcher->Accrual());
return text;
}

QString ResearcherModel::formatStatus() const
Expand Down
2 changes: 2 additions & 0 deletions src/qt/researcher/researchermodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ class ResearcherModel : public QObject

void showWizard(WalletModel* wallet_model);
void setTheme(const QString& theme_name);
void setMaskAccrualAndMagnitude(bool privacy);

bool configuredForInvestorMode() const;
bool outOfSync() const;
Expand Down Expand Up @@ -120,6 +121,7 @@ class ResearcherModel : public QObject
bool m_configured_for_investor_mode;
bool m_wizard_open;
bool m_out_of_sync;
bool m_privacy_enabled;
QString m_theme_suffix;

void subscribeToCoreSignals();
Expand Down

0 comments on commit e952ab8

Please sign in to comment.