Improve balance formatting in the UI.

This commit is contained in:
lukealonso 2017-12-28 21:31:28 -08:00 committed by clemahieu
commit a695d0f52b
6 changed files with 193 additions and 16 deletions

View file

@ -13,6 +13,43 @@ TEST (uint128_union, decode_dec)
ASSERT_EQ (16, value.bytes [15]);
}
struct test_punct : std::moneypunct<char> {
pattern do_pos_format () const { return { {value, none, none, none} }; }
int do_frac_digits () const { return 0; }
char_type do_decimal_point () const { return '+'; }
char_type do_thousands_sep () const { return '-'; }
string_type do_grouping () const { return "\3\4"; }
};
TEST (uint128_union, balance_format)
{
ASSERT_EQ ("0", rai::amount (rai::uint128_t ("0")).format_balance (rai::Mxrb_ratio, 0, false));
ASSERT_EQ ("0", rai::amount (rai::uint128_t ("0")).format_balance (rai::Mxrb_ratio, 2, true));
ASSERT_EQ ("340,282,366", rai::amount (rai::uint128_t ("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")).format_balance (rai::Mxrb_ratio, 0, true));
ASSERT_EQ ("340,282,366.920938463463374607431768211455", rai::amount (rai::uint128_t ("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")).format_balance (rai::Mxrb_ratio, 64, true));
ASSERT_EQ ("340,282,366,920,938,463,463,374,607,431,768,211,455", rai::amount (rai::uint128_t ("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")).format_balance (1, 4, true));
ASSERT_EQ ("340,282,366", rai::amount (rai::uint128_t ("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE")).format_balance (rai::Mxrb_ratio, 0, true));
ASSERT_EQ ("340,282,366.920938463463374607431768211454", rai::amount (rai::uint128_t ("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE")).format_balance (rai::Mxrb_ratio, 64, true));
ASSERT_EQ ("340282366920938463463374607431768211454", rai::amount (rai::uint128_t ("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE")).format_balance (1, 4, false));
ASSERT_EQ ("170,141,183", rai::amount (rai::uint128_t ("0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE")).format_balance (rai::Mxrb_ratio, 0, true));
ASSERT_EQ ("170,141,183.460469231731687303715884105726", rai::amount (rai::uint128_t ("0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE")).format_balance (rai::Mxrb_ratio, 64, true));
ASSERT_EQ ("170141183460469231731687303715884105726", rai::amount (rai::uint128_t ("0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE")).format_balance (1, 4, false));
ASSERT_EQ ("1", rai::amount (rai::uint128_t ("1000000000000000000000000000000")).format_balance (rai::Mxrb_ratio, 2, true));
ASSERT_EQ ("1.2", rai::amount (rai::uint128_t ("1200000000000000000000000000000")).format_balance (rai::Mxrb_ratio, 2, true));
ASSERT_EQ ("1.23", rai::amount (rai::uint128_t ("1230000000000000000000000000000")).format_balance (rai::Mxrb_ratio, 2, true));
ASSERT_EQ ("1.2", rai::amount (rai::uint128_t ("1230000000000000000000000000000")).format_balance (rai::Mxrb_ratio, 1, true));
ASSERT_EQ ("1", rai::amount (rai::uint128_t ("1230000000000000000000000000000")).format_balance (rai::Mxrb_ratio, 0, true));
ASSERT_EQ ("< 0.01", rai::amount (rai::xrb_ratio * 10).format_balance (rai::Mxrb_ratio, 2, true));
ASSERT_EQ ("< 0.1", rai::amount (rai::xrb_ratio * 10).format_balance (rai::Mxrb_ratio, 1, true));
ASSERT_EQ ("< 1", rai::amount (rai::xrb_ratio * 10).format_balance (rai::Mxrb_ratio, 0, true));
ASSERT_EQ ("< 0.01", rai::amount (rai::xrb_ratio * 9999).format_balance (rai::Mxrb_ratio, 2, true));
ASSERT_EQ ("0.01", rai::amount (rai::xrb_ratio * 10000).format_balance (rai::Mxrb_ratio, 2, true));
ASSERT_EQ ("123456789", rai::amount (rai::Mxrb_ratio * 123456789).format_balance (rai::Mxrb_ratio, 2, false));
ASSERT_EQ ("123,456,789", rai::amount (rai::Mxrb_ratio * 123456789).format_balance (rai::Mxrb_ratio, 2, true));
ASSERT_EQ ("123,456,789.12", rai::amount (rai::Mxrb_ratio * 123456789 + rai::kxrb_ratio * 123).format_balance (rai::Mxrb_ratio, 2, true));
ASSERT_EQ ("12-3456-789+123", rai::amount (rai::Mxrb_ratio * 123456789 + rai::kxrb_ratio * 123).format_balance (rai::Mxrb_ratio, 4, true, std::locale (std::cout.getloc (), new test_punct)));
}
TEST (unions, identity)
{
ASSERT_EQ (1, rai::uint128_union (1).number ().convert_to <uint8_t> ());

View file

@ -311,6 +311,10 @@ bool rai::uint256_union::decode_dec (std::string const & text)
{
stream >> number_l;
*this = number_l;
if (!stream.eof ())
{
result = true;
}
}
catch (std::runtime_error &)
{
@ -570,6 +574,10 @@ bool rai::uint128_union::decode_dec (std::string const & text)
{
stream >> number_l;
*this = number_l;
if (!stream.eof ())
{
result = true;
}
}
catch (std::runtime_error &)
{
@ -579,6 +587,114 @@ bool rai::uint128_union::decode_dec (std::string const & text)
return result;
}
void format_frac(std::ostringstream & stream, rai::uint128_t value, rai::uint128_t scale, int precision) {
auto reduce = scale;
auto rem = value;
while (reduce > 1 && rem > 0 && precision > 0) {
reduce /= 10;
auto val = rem / reduce;
rem -= val * reduce;
stream << val;
precision--;
}
}
void format_dec(std::ostringstream & stream, rai::uint128_t value, char group_sep, const std::string & groupings) {
auto largestPow10 = rai::uint256_t (1);
int dec_count = 1;
while (1) {
auto next = largestPow10 * 10;
if (next > value) {
break;
}
largestPow10 = next;
dec_count++;
}
if (dec_count > 39) {
// Impossible.
return;
}
// This could be cached per-locale.
bool emit_group[39];
if (group_sep != 0) {
int group_index = 0;
int group_count = 0;
for (int i = 0; i < dec_count; i++) {
int groupMax = groupings [group_index];
group_count++;
if (group_count > groupings [group_index]) {
group_index = std::min (group_index + 1, (int)groupings.length() - 1);
group_count = 1;
emit_group [i] = true;
} else {
emit_group [i] = false;
}
}
}
auto reduce = rai::uint128_t(largestPow10);
rai::uint128_t rem = value;
while (reduce > 0) {
auto val = rem / reduce;
rem -= val * reduce;
stream << val;
dec_count--;
if (group_sep != 0 && emit_group [dec_count] && reduce > 1) {
stream << group_sep;
}
reduce /= 10;
}
}
std::string format_balance (rai::uint128_t balance, rai::uint128_t scale, int precision, bool group_digits, char thousands_sep, char decimal_point, std::string & grouping)
{
std::ostringstream stream;
auto int_part = balance / scale;
auto frac_part = balance % scale;
auto prec_scale = scale;
for (int i = 0; i < precision; i++) {
prec_scale /= 10;
}
if (int_part == 0 && frac_part > 0 && frac_part / prec_scale == 0) {
// Display e.g. "< 0.01" rather than 0.
stream << "< ";
if (precision > 0) {
stream << "0";
stream << decimal_point;
for (int i = 0; i < precision - 1; i++) {
stream << "0";
}
}
stream << "1";
} else {
format_dec (stream, int_part, group_digits && grouping.length () > 0 ? thousands_sep : 0, grouping);
if (precision > 0 && frac_part > 0) {
stream << decimal_point;
format_frac (stream, frac_part, scale, precision);
}
}
return stream.str();
}
std::string rai::uint128_union::format_balance(rai::uint128_t scale, int precision, bool group_digits)
{
auto thousands_sep = std::use_facet< std::numpunct<char> >(std::locale ()).thousands_sep ();
auto decimal_point = std::use_facet< std::numpunct<char> >(std::locale ()).decimal_point ();
std::string grouping = "\3";
return ::format_balance (number (), scale, precision, group_digits, thousands_sep, decimal_point, grouping);
}
std::string rai::uint128_union::format_balance (rai::uint128_t scale, int precision, bool group_digits, const std::locale & locale)
{
auto thousands_sep = std::use_facet< std::moneypunct<char> >(locale).thousands_sep ();
auto decimal_point = std::use_facet< std::moneypunct<char> >(locale).decimal_point ();
std::string grouping = std::use_facet< std::moneypunct<char> >(locale).grouping ();
return ::format_balance (number (), scale, precision, group_digits, thousands_sep, decimal_point, grouping);
}
void rai::uint128_union::clear ()
{
qwords.fill (0);

View file

@ -35,6 +35,8 @@ public:
bool decode_hex (std::string const &);
void encode_dec (std::string &) const;
bool decode_dec (std::string const &);
std::string format_balance (rai::uint128_t scale, int precision, bool group_digits);
std::string format_balance (rai::uint128_t scale, int precision, bool group_digits, const std::locale & locale);
rai::uint128_t number () const;
void clear ();
bool is_zero () const;

View file

@ -104,10 +104,10 @@ wallet (wallet_a)
void rai_qt::self_pane::refresh_balance ()
{
auto balance (wallet.node.balance_pending (wallet.account));
auto final_text (std::string ("Balance (XRB): ") + (balance.first / wallet.rendering_ratio).convert_to <std::string> ());
auto final_text (std::string ("Balance: ") + wallet.format_balance (balance.first));
if (!balance.second.is_zero ())
{
final_text += "\nPending: " + (balance.second / wallet.rendering_ratio).convert_to <std::string> ();
final_text += "\nPending: " + wallet.format_balance (balance.second);
}
wallet.self.balance_label->setText (QString (final_text.c_str ()));
}
@ -253,10 +253,10 @@ void rai_qt::accounts::refresh_wallet_balance ()
balance = balance + (this->wallet.node.ledger.account_balance (transaction, key));
pending = pending + (this->wallet.node.ledger.account_pending (transaction, key));
}
auto final_text (std::string ("Wallet balance (XRB): ") + (balance / this->wallet.rendering_ratio).convert_to <std::string> ());
auto final_text (std::string ("Wallet balance (XRB): ") + wallet.format_balance (balance));
if (!pending.is_zero ())
{
final_text += "\nWallet pending: " + (pending / this->wallet.rendering_ratio).convert_to <std::string> ();
final_text += "\nWallet pending: " + wallet.format_balance (pending);
}
wallet_balance_label->setText (QString (final_text.c_str ()));
this->wallet.node.alarm.add (std::chrono::system_clock::now () + std::chrono::seconds (60), [this] ()
@ -295,8 +295,7 @@ void rai_qt::accounts::refresh ()
if (display)
{
QList <QStandardItem *> items;
std::string balance;
rai::amount (balance_amount / wallet.rendering_ratio).encode_dec (balance);
std::string balance = wallet.format_balance (balance_amount);
items.push_back (new QStandardItem (balance.c_str ()));
auto account (new QStandardItem (QString (key.to_account ().c_str ())));
account->setForeground (brush);
@ -467,7 +466,7 @@ wallet (wallet_a)
});
}
rai_qt::history::history (rai::ledger & ledger_a, rai::account const & account_a, rai::uint128_t const & rendering_ratio_a) :
rai_qt::history::history (rai::ledger & ledger_a, rai::account const & account_a, rai_qt::wallet & wallet_a) :
window (new QWidget),
layout (new QVBoxLayout),
model (new QStandardItemModel),
@ -478,7 +477,7 @@ tx_label (new QLabel ("Account history count:")),
tx_count (new QSpinBox),
ledger (ledger_a),
account (account_a),
rendering_ratio (rendering_ratio_a)
wallet (wallet_a)
{/*
tx_count->setRange (1, 256);
tx_layout->addWidget (tx_label);
@ -564,7 +563,7 @@ void rai_qt::history::refresh ()
block->visit (visitor);
items.push_back (new QStandardItem (QString (visitor.type.c_str ())));
items.push_back (new QStandardItem (QString (visitor.account.to_account ().c_str ())));
items.push_back (new QStandardItem (QString (rai::amount (visitor.amount / rendering_ratio).to_string_dec ().c_str ())));
items.push_back (new QStandardItem (QString (wallet.format_balance (visitor.amount).c_str())));
items.push_back (new QStandardItem (QString (hash.to_string ().c_str ())));
hash = block->previous ();
model->appendRow (items);
@ -678,7 +677,7 @@ refresh (new QPushButton ("Refresh")),
balance_window (new QWidget),
balance_layout (new QHBoxLayout),
balance_label (new QLabel),
history (wallet_a.wallet_m->node.ledger, account, wallet_a.rendering_ratio),
history (wallet_a.wallet_m->node.ledger, account, wallet_a),
back (new QPushButton ("Back")),
account (wallet_a.account),
wallet (wallet_a)
@ -706,10 +705,10 @@ wallet (wallet_a)
show_line_ok (*account_line);
this->history.refresh ();
auto balance (this->wallet.node.balance_pending (account));
auto final_text (std::string ("Balance (XRB): ") + (balance.first / this->wallet.rendering_ratio).convert_to <std::string> ());
auto final_text (std::string ("Balance (XRB): ") + wallet.format_balance (balance.first));
if (!balance.second.is_zero ())
{
final_text += "\nPending: " + (balance.second / this->wallet.rendering_ratio).convert_to <std::string> ();
final_text += "\nPending: " + wallet.format_balance (balance.second);
}
balance_label->setText (QString (final_text.c_str ()));
}
@ -840,7 +839,7 @@ node (node_a),
wallet_m (wallet_a),
account (account_a),
processor (processor_a),
history (node.ledger, account, rendering_ratio),
history (node.ledger, account, *this),
accounts (*this),
self (*this, account_a),
settings (*this),
@ -1257,6 +1256,18 @@ void rai_qt::wallet::change_rendering_ratio (rai::uint128_t const & rendering_ra
}));
}
std::string rai_qt::wallet::format_balance (rai::uint128_t const & balance) const
{
auto balance_str = rai::amount (balance).format_balance (rendering_ratio, 2, true, std::locale (""));
auto unit = std::string ("XRB");
if (rendering_ratio == rai::kxrb_ratio) {
unit = std::string ("kxrb");
} else if (rendering_ratio == rai::xrb_ratio) {
unit = std::string ("xrb");
}
return balance_str + " " + unit;
}
void rai_qt::wallet::push_main_stack (QWidget * widget_a)
{
main_stack->addWidget (widget_a);

View file

@ -207,7 +207,7 @@ namespace rai_qt {
class history
{
public:
history (rai::ledger &, rai::account const &, rai::uint128_t const &);
history (rai::ledger &, rai::account const &, rai_qt::wallet &);
void refresh ();
QWidget * window;
QVBoxLayout * layout;
@ -219,7 +219,7 @@ namespace rai_qt {
QSpinBox * tx_count;
rai::ledger & ledger;
rai::account const & account;
rai::uint128_t const & rendering_ratio;
rai_qt::wallet & wallet;
};
class block_viewer
{
@ -288,6 +288,7 @@ namespace rai_qt {
void update_connected ();
void empty_password ();
void change_rendering_ratio (rai::uint128_t const &);
std::string format_balance (rai::uint128_t const &) const;
rai::uint128_t rendering_ratio;
rai::node & node;
std::shared_ptr <rai::wallet> wallet_m;

View file

@ -436,6 +436,16 @@ TEST (wallet, create_change)
TEST (history, short_text)
{
bool init;
rai_qt::eventloop_processor processor;
rai::keypair key;
rai::system system (24000, 1);
system.wallet (0)->insert_adhoc (key.prv);
rai::account account;
{
rai::transaction transaction (system.nodes [0]->store.environment, nullptr, false);
account = system.account (transaction, 0);
}
auto wallet (std::make_shared <rai_qt::wallet> (*test_application, processor, *system.nodes [0], system.wallet (0), account));
rai::block_store store (init, rai::unique_path ());
ASSERT_TRUE (!init);
rai::genesis genesis;
@ -451,7 +461,7 @@ TEST (history, short_text)
rai::change_block change (receive.hash (), key.pub, rai::test_genesis_key.prv, rai::test_genesis_key.pub, 0);
ASSERT_EQ (rai::process_result::progress, ledger.process (transaction, change).code);
}
rai_qt::history history (ledger, rai::test_genesis_key.pub, rai::Gxrb_ratio);
rai_qt::history history (ledger, rai::test_genesis_key.pub, *wallet);
history.refresh ();
ASSERT_EQ (4, history.model->rowCount ());
}