[Qt] New status bar Unit Display Control and related changes.

- New status bar control shows the current Unit of Display.
  When clicked (left,or right button) it shows a context menu
  that allows the user to switch the current Unit of Display (BTC, mBTC, uBTC)
- Recent Requests and Transaction Table headers are now updated when
  unit of display is changed, because their "Amount" column now displays the
  current unit of display.
- Takes care of issue #3970 Units in transaction export csv file.
- Small refactors for reusability.
- Demo Video https://www.youtube.com/watch?v=wwcr0Yh68go&list=UUG3jF2hgofmLWP0tRPisQAQ
- changes after Diapolo's feedback. Have not been able to build after last pool, issues with boost on MacOSX, will test on Ubuntu these changes.
- removed return statement on switch
- renamed onDisplayUnitsChanged(int) to updateDisplayUnit(int)
- now getAmountColumnTitle(int unit) takes a simple unit parameter. moved to BitcoinUnits.
This commit is contained in:
gubatron 2014-06-07 02:20:22 -04:00
parent 343feecf56
commit 8969828d06
11 changed files with 187 additions and 7 deletions

View file

@ -28,6 +28,7 @@
#include <iostream> #include <iostream>
#include <QAction>
#include <QApplication> #include <QApplication>
#include <QDateTime> #include <QDateTime>
#include <QDesktopWidget> #include <QDesktopWidget>
@ -39,6 +40,7 @@
#include <QMenuBar> #include <QMenuBar>
#include <QMessageBox> #include <QMessageBox>
#include <QMimeData> #include <QMimeData>
#include <QPoint>
#include <QProgressBar> #include <QProgressBar>
#include <QProgressDialog> #include <QProgressDialog>
#include <QSettings> #include <QSettings>
@ -49,6 +51,8 @@
#include <QToolBar> #include <QToolBar>
#include <QVBoxLayout> #include <QVBoxLayout>
#if QT_VERSION < 0x050000 #if QT_VERSION < 0x050000
#include <QUrl> #include <QUrl>
#include <QTextDocument> #include <QTextDocument>
@ -156,10 +160,13 @@ BitcoinGUI::BitcoinGUI(bool fIsTestnet, QWidget *parent) :
QHBoxLayout *frameBlocksLayout = new QHBoxLayout(frameBlocks); QHBoxLayout *frameBlocksLayout = new QHBoxLayout(frameBlocks);
frameBlocksLayout->setContentsMargins(3,0,3,0); frameBlocksLayout->setContentsMargins(3,0,3,0);
frameBlocksLayout->setSpacing(3); frameBlocksLayout->setSpacing(3);
unitDisplayControl = new UnitDisplayStatusBarControl();
labelEncryptionIcon = new QLabel(); labelEncryptionIcon = new QLabel();
labelConnectionsIcon = new QLabel(); labelConnectionsIcon = new QLabel();
labelBlocksIcon = new QLabel(); labelBlocksIcon = new QLabel();
frameBlocksLayout->addStretch(); frameBlocksLayout->addStretch();
frameBlocksLayout->addWidget(unitDisplayControl);
frameBlocksLayout->addStretch();
frameBlocksLayout->addWidget(labelEncryptionIcon); frameBlocksLayout->addWidget(labelEncryptionIcon);
frameBlocksLayout->addStretch(); frameBlocksLayout->addStretch();
frameBlocksLayout->addWidget(labelConnectionsIcon); frameBlocksLayout->addWidget(labelConnectionsIcon);
@ -420,6 +427,8 @@ void BitcoinGUI::setClientModel(ClientModel *clientModel)
walletFrame->setClientModel(clientModel); walletFrame->setClientModel(clientModel);
} }
#endif #endif
this->unitDisplayControl->setOptionsModel(clientModel->getOptionsModel());
} }
} }
@ -1000,3 +1009,72 @@ void BitcoinGUI::unsubscribeFromCoreSignals()
// Disconnect signals from client // Disconnect signals from client
uiInterface.ThreadSafeMessageBox.disconnect(boost::bind(ThreadSafeMessageBox, this, _1, _2, _3)); uiInterface.ThreadSafeMessageBox.disconnect(boost::bind(ThreadSafeMessageBox, this, _1, _2, _3));
} }
UnitDisplayStatusBarControl::UnitDisplayStatusBarControl():QLabel()
{
optionsModel = 0;
createContextMenu();
setStyleSheet("font:11pt; color: #333333");
setToolTip(tr("Unit to show amounts in. Click to select another unit."));
}
/** So that it responds to left-button clicks */
void UnitDisplayStatusBarControl::mousePressEvent(QMouseEvent *event)
{
onDisplayUnitsClicked(event->pos());
}
/** Creates context menu, its actions, and wires up all the relevant signals for mouse events. */
void UnitDisplayStatusBarControl::createContextMenu()
{
menu = new QMenu();
foreach(BitcoinUnits::Unit u, BitcoinUnits::availableUnits())
{
QAction *menuAction = new QAction(QString(BitcoinUnits::name(u)), this);
menuAction->setData(QVariant(u));
menu->addAction(menuAction);
}
connect(menu,SIGNAL(triggered(QAction*)),this,SLOT(onMenuSelection(QAction*)));
// what happens on right click.
setContextMenuPolicy(Qt::CustomContextMenu);
connect(this,SIGNAL(customContextMenuRequested(const QPoint&)),this,SLOT(onDisplayUnitsClicked(const QPoint&)));
}
/** Lets the control know about the Options Model (and its signals) */
void UnitDisplayStatusBarControl::setOptionsModel(OptionsModel *optionsModel)
{
if (optionsModel)
{
this->optionsModel = optionsModel;
// be aware of a display unit change reported by the OptionsModel object.
connect(optionsModel,SIGNAL(displayUnitChanged(int)),this,SLOT(updateDisplayUnit(int)));
// initialize the display units label with the current value in the model.
updateDisplayUnit(optionsModel->getDisplayUnit());
}
}
/** When Display Units are changed on OptionsModel it will refresh the display text of the control on the status bar */
void UnitDisplayStatusBarControl::updateDisplayUnit(int newUnits)
{
setText(BitcoinUnits::name(newUnits));
}
/** Shows context menu with Display Unit options by the mouse coordinates */
void UnitDisplayStatusBarControl::onDisplayUnitsClicked(const QPoint& point)
{
QPoint globalPos = mapToGlobal(point);
menu->exec(globalPos);
}
/** Tells underlying optionsModel to update its current display unit. */
void UnitDisplayStatusBarControl::onMenuSelection(QAction* action)
{
if (action)
{
optionsModel->setDisplayUnit(action->data());
}
}

View file

@ -9,12 +9,16 @@
#include "config/bitcoin-config.h" #include "config/bitcoin-config.h"
#endif #endif
#include <QLabel>
#include <QMainWindow> #include <QMainWindow>
#include <QMap> #include <QMap>
#include <QMenu>
#include <QPoint>
#include <QSystemTrayIcon> #include <QSystemTrayIcon>
class ClientModel; class ClientModel;
class Notificator; class Notificator;
class OptionsModel;
class RPCConsole; class RPCConsole;
class SendCoinsRecipient; class SendCoinsRecipient;
class WalletFrame; class WalletFrame;
@ -22,9 +26,13 @@ class WalletModel;
class CWallet; class CWallet;
class UnitDisplayStatusBarControl;
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QAction; class QAction;
class QLabel; class QLabel;
class QMenu;
class QPoint;
class QProgressBar; class QProgressBar;
class QProgressDialog; class QProgressDialog;
QT_END_NAMESPACE QT_END_NAMESPACE
@ -69,6 +77,7 @@ private:
ClientModel *clientModel; ClientModel *clientModel;
WalletFrame *walletFrame; WalletFrame *walletFrame;
UnitDisplayStatusBarControl *unitDisplayControl;
QLabel *labelEncryptionIcon; QLabel *labelEncryptionIcon;
QLabel *labelConnectionsIcon; QLabel *labelConnectionsIcon;
QLabel *labelBlocksIcon; QLabel *labelBlocksIcon;
@ -198,4 +207,32 @@ private slots:
void showProgress(const QString &title, int nProgress); void showProgress(const QString &title, int nProgress);
}; };
class UnitDisplayStatusBarControl : public QLabel
{
Q_OBJECT
public:
explicit UnitDisplayStatusBarControl();
/** Lets the control know about the Options Model (and its signals) */
void setOptionsModel(OptionsModel *optionsModel);
protected:
/** So that it responds to left-button clicks */
void mousePressEvent(QMouseEvent *event);
private:
OptionsModel *optionsModel;
QMenu* menu;
/** Shows context menu with Display Unit options by the mouse coordinates */
void onDisplayUnitsClicked(const QPoint& point);
/** Creates context menu, its actions, and wires up all the relevant signals for mouse events. */
void createContextMenu();
private slots:
/** When Display Units are changed on OptionsModel it will refresh the display text of the control on the status bar */
void updateDisplayUnit(int newUnits);
/** Tells underlying optionsModel to update its current display unit. */
void onMenuSelection(QAction* action);
};
#endif // BITCOINGUI_H #endif // BITCOINGUI_H

View file

@ -169,6 +169,16 @@ bool BitcoinUnits::parse(int unit, const QString &value, qint64 *val_out)
return ok; return ok;
} }
QString BitcoinUnits::getAmountColumnTitle(int unit)
{
QString amountTitle = QObject::tr("Amount");
if (BitcoinUnits::valid(unit))
{
amountTitle += " ("+BitcoinUnits::name(unit) + ")";
}
return amountTitle;
}
int BitcoinUnits::rowCount(const QModelIndex &parent) const int BitcoinUnits::rowCount(const QModelIndex &parent) const
{ {
Q_UNUSED(parent); Q_UNUSED(parent);

View file

@ -54,6 +54,8 @@ public:
static QString formatWithUnit(int unit, qint64 amount, bool plussign=false); static QString formatWithUnit(int unit, qint64 amount, bool plussign=false);
//! Parse string to coin amount //! Parse string to coin amount
static bool parse(int unit, const QString &value, qint64 *val_out); static bool parse(int unit, const QString &value, qint64 *val_out);
//! Gets title for amount column including current display unit if optionsModel reference available */
static QString getAmountColumnTitle(int unit);
///@} ///@}
//! @name AbstractListModel implementation //! @name AbstractListModel implementation

View file

@ -308,9 +308,7 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in
break; break;
#endif #endif
case DisplayUnit: case DisplayUnit:
nDisplayUnit = value.toInt(); setDisplayUnit(value);
settings.setValue("nDisplayUnit", nDisplayUnit);
emit displayUnitChanged(nDisplayUnit);
break; break;
case DisplayAddresses: case DisplayAddresses:
bDisplayAddresses = value.toBool(); bDisplayAddresses = value.toBool();
@ -356,11 +354,24 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in
break; break;
} }
} }
emit dataChanged(index, index); emit dataChanged(index, index);
return successful; return successful;
} }
/** Updates current unit in memory, settings and emits displayUnitChanged(newUnit) signal */
void OptionsModel::setDisplayUnit(const QVariant &value)
{
if (!value.isNull())
{
QSettings settings;
nDisplayUnit = value.toInt();
settings.setValue("nDisplayUnit", nDisplayUnit);
emit displayUnitChanged(nDisplayUnit);
}
}
bool OptionsModel::getProxySettings(QNetworkProxy& proxy) const bool OptionsModel::getProxySettings(QNetworkProxy& proxy) const
{ {
// Directly query current base proxy, because // Directly query current base proxy, because

View file

@ -52,6 +52,8 @@ public:
int rowCount(const QModelIndex & parent = QModelIndex()) const; int rowCount(const QModelIndex & parent = QModelIndex()) const;
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const; QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const;
bool setData(const QModelIndex & index, const QVariant & value, int role = Qt::EditRole); bool setData(const QModelIndex & index, const QVariant & value, int role = Qt::EditRole);
/** Updates current unit in memory, settings and emits displayUnitChanged(newUnit) signal */
void setDisplayUnit(const QVariant &value);
/* Explicit getters */ /* Explicit getters */
bool getMinimizeToTray() { return fMinimizeToTray; } bool getMinimizeToTray() { return fMinimizeToTray; }

View file

@ -21,7 +21,9 @@ RecentRequestsTableModel::RecentRequestsTableModel(CWallet *wallet, WalletModel
addNewRequest(request); addNewRequest(request);
/* These columns must match the indices in the ColumnIndex enumeration */ /* These columns must match the indices in the ColumnIndex enumeration */
columns << tr("Date") << tr("Label") << tr("Message") << tr("Amount"); columns << tr("Date") << tr("Label") << tr("Message") << getAmountTitle();
connect(walletModel->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit()));
} }
RecentRequestsTableModel::~RecentRequestsTableModel() RecentRequestsTableModel::~RecentRequestsTableModel()
@ -101,6 +103,24 @@ QVariant RecentRequestsTableModel::headerData(int section, Qt::Orientation orien
return QVariant(); return QVariant();
} }
/** Updates the column title to "Amount (DisplayUnit)" and emits headerDataChanged() signal for table headers to react. */
void RecentRequestsTableModel::updateAmountColumnTitle()
{
columns[Amount] = getAmountTitle();
emit headerDataChanged(Qt::Horizontal,Amount,Amount);
}
/** Gets title for amount column including current display unit if optionsModel reference available. */
QString RecentRequestsTableModel::getAmountTitle()
{
QString amountTitle = tr("Amount");
if (this->walletModel->getOptionsModel() != NULL)
{
amountTitle += " ("+BitcoinUnits::name(this->walletModel->getOptionsModel()->getDisplayUnit()) + ")";
}
return amountTitle;
}
QModelIndex RecentRequestsTableModel::index(int row, int column, const QModelIndex &parent) const QModelIndex RecentRequestsTableModel::index(int row, int column, const QModelIndex &parent) const
{ {
Q_UNUSED(parent); Q_UNUSED(parent);
@ -185,6 +205,11 @@ void RecentRequestsTableModel::sort(int column, Qt::SortOrder order)
emit dataChanged(index(0, 0, QModelIndex()), index(list.size() - 1, NUMBER_OF_COLUMNS - 1, QModelIndex())); emit dataChanged(index(0, 0, QModelIndex()), index(list.size() - 1, NUMBER_OF_COLUMNS - 1, QModelIndex()));
} }
void RecentRequestsTableModel::updateDisplayUnit()
{
updateAmountColumnTitle();
}
bool RecentRequestEntryLessThan::operator()(RecentRequestEntry &left, RecentRequestEntry &right) const bool RecentRequestEntryLessThan::operator()(RecentRequestEntry &left, RecentRequestEntry &right) const
{ {
RecentRequestEntry *pLeft = &left; RecentRequestEntry *pLeft = &left;

View file

@ -91,12 +91,18 @@ public:
public slots: public slots:
void sort(int column, Qt::SortOrder order = Qt::AscendingOrder); void sort(int column, Qt::SortOrder order = Qt::AscendingOrder);
void updateDisplayUnit();
private: private:
WalletModel *walletModel; WalletModel *walletModel;
QStringList columns; QStringList columns;
QList<RecentRequestEntry> list; QList<RecentRequestEntry> list;
int64_t nReceiveRequestsMaxId; int64_t nReceiveRequestsMaxId;
/** Updates the column title to "Amount (DisplayUnit)" and emits headerDataChanged() signal for table headers to react. */
void updateAmountColumnTitle();
/** Gets title for amount column including current display unit if optionsModel reference available. */
QString getAmountTitle();
}; };
#endif #endif

View file

@ -235,8 +235,7 @@ TransactionTableModel::TransactionTableModel(CWallet* wallet, WalletModel *paren
walletModel(parent), walletModel(parent),
priv(new TransactionTablePriv(wallet, this)) priv(new TransactionTablePriv(wallet, this))
{ {
columns << QString() << tr("Date") << tr("Type") << tr("Address") << tr("Amount"); columns << QString() << tr("Date") << tr("Type") << tr("Address") << BitcoinUnits::getAmountColumnTitle(walletModel->getOptionsModel()->getDisplayUnit());
priv->refreshWallet(); priv->refreshWallet();
connect(walletModel->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); connect(walletModel->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit()));
@ -247,6 +246,13 @@ TransactionTableModel::~TransactionTableModel()
delete priv; delete priv;
} }
/** Updates the column title to "Amount (DisplayUnit)" and emits headerDataChanged() signal for table headers to react. */
void TransactionTableModel::updateAmountColumnTitle()
{
columns[Amount] = BitcoinUnits::getAmountColumnTitle(walletModel->getOptionsModel()->getDisplayUnit());
emit headerDataChanged(Qt::Horizontal,Amount,Amount);
}
void TransactionTableModel::updateTransaction(const QString &hash, int status) void TransactionTableModel::updateTransaction(const QString &hash, int status)
{ {
uint256 updated; uint256 updated;
@ -624,5 +630,6 @@ QModelIndex TransactionTableModel::index(int row, int column, const QModelIndex
void TransactionTableModel::updateDisplayUnit() void TransactionTableModel::updateDisplayUnit()
{ {
// emit dataChanged to update Amount column with the current unit // emit dataChanged to update Amount column with the current unit
updateAmountColumnTitle();
emit dataChanged(index(0, Amount), index(priv->size()-1, Amount)); emit dataChanged(index(0, Amount), index(priv->size()-1, Amount));
} }

View file

@ -87,6 +87,8 @@ public slots:
void updateTransaction(const QString &hash, int status); void updateTransaction(const QString &hash, int status);
void updateConfirmations(); void updateConfirmations();
void updateDisplayUnit(); void updateDisplayUnit();
/** Updates the column title to "Amount (DisplayUnit)" and emits headerDataChanged() signal for table headers to react. */
void updateAmountColumnTitle();
friend class TransactionTablePriv; friend class TransactionTablePriv;
}; };

View file

@ -309,7 +309,7 @@ void TransactionView::exportClicked()
writer.addColumn(tr("Type"), TransactionTableModel::Type, Qt::EditRole); writer.addColumn(tr("Type"), TransactionTableModel::Type, Qt::EditRole);
writer.addColumn(tr("Label"), 0, TransactionTableModel::LabelRole); writer.addColumn(tr("Label"), 0, TransactionTableModel::LabelRole);
writer.addColumn(tr("Address"), 0, TransactionTableModel::AddressRole); writer.addColumn(tr("Address"), 0, TransactionTableModel::AddressRole);
writer.addColumn(tr("Amount"), 0, TransactionTableModel::FormattedAmountRole); writer.addColumn(BitcoinUnits::getAmountColumnTitle(model->getOptionsModel()->getDisplayUnit()), 0, TransactionTableModel::FormattedAmountRole);
writer.addColumn(tr("ID"), 0, TransactionTableModel::TxIDRole); writer.addColumn(tr("ID"), 0, TransactionTableModel::TxIDRole);
if(!writer.write()) { if(!writer.write()) {