adding asset fetching
This commit is contained in:
commit
b9f6ee4f2c
12 changed files with 256 additions and 92 deletions
|
|
@ -174,10 +174,10 @@ void witness_plugin::block_production_loop()
|
||||||
_production_enabled = true;
|
_production_enabled = true;
|
||||||
|
|
||||||
// is anyone scheduled to produce now or one second in the future?
|
// is anyone scheduled to produce now or one second in the future?
|
||||||
uint32_t slot = db.get_slot_at_time( graphene::time::now() + fc::seconds(1) );
|
const fc::time_point_sec now = graphene::time::now();
|
||||||
|
uint32_t slot = db.get_slot_at_time( now );
|
||||||
graphene::chain::witness_id_type scheduled_witness = db.get_scheduled_witness( slot ).first;
|
graphene::chain::witness_id_type scheduled_witness = db.get_scheduled_witness( slot ).first;
|
||||||
fc::time_point_sec scheduled_time = db.get_slot_time( slot );
|
fc::time_point_sec scheduled_time = db.get_slot_time( slot );
|
||||||
fc::time_point_sec now = graphene::time::now();
|
|
||||||
graphene::chain::public_key_type scheduled_key = scheduled_witness( db ).signing_key;
|
graphene::chain::public_key_type scheduled_key = scheduled_witness( db ).signing_key;
|
||||||
|
|
||||||
auto is_scheduled = [&]()
|
auto is_scheduled = [&]()
|
||||||
|
|
@ -194,7 +194,7 @@ void witness_plugin::block_production_loop()
|
||||||
uint32_t prate = db.witness_participation_rate();
|
uint32_t prate = db.witness_participation_rate();
|
||||||
if( prate < _required_witness_participation )
|
if( prate < _required_witness_participation )
|
||||||
{
|
{
|
||||||
elog("Not producing block because node appers to be on a minority fork with only ${x}% witness participation",
|
elog("Not producing block because node appears to be on a minority fork with only ${x}% witness participation",
|
||||||
("x",uint32_t(100*uint64_t(prate) / GRAPHENE_1_PERCENT) ) );
|
("x",uint32_t(100*uint64_t(prate) / GRAPHENE_1_PERCENT) ) );
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -212,21 +212,19 @@ void witness_plugin::block_production_loop()
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// the local clock must be at least 1 second ahead of
|
// the local clock must be at least 1 second ahead of head_block_time.
|
||||||
// head_block_time.
|
if( (now - db.head_block_time()).to_seconds() < GRAPHENE_MIN_BLOCK_INTERVAL ) {
|
||||||
if( (now - db.head_block_time()).to_seconds() <= 1 ) {
|
|
||||||
elog("Not producing block because head block is less than a second old.");
|
elog("Not producing block because head block is less than a second old.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// the local clock must be within 500 milliseconds of
|
// the local clock must be within 500 milliseconds of
|
||||||
// the scheduled production time.
|
// the scheduled production time.
|
||||||
if( llabs((scheduled_time - now).count()) > fc::milliseconds(250).count() ) {
|
if( llabs((scheduled_time - now).count()) > fc::milliseconds( 500 ).count() ) {
|
||||||
elog("Not producing block because network time is not within 250ms of scheduled block time.");
|
elog("Not producing block because network time is not within 250ms of scheduled block time.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// we must know the private key corresponding to the witness's
|
// we must know the private key corresponding to the witness's
|
||||||
// published block production key.
|
// published block production key.
|
||||||
if( _private_keys.find( scheduled_key ) == _private_keys.end() ) {
|
if( _private_keys.find( scheduled_key ) == _private_keys.end() ) {
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,8 @@ file(GLOB QML qml/*)
|
||||||
qt5_add_resources(QML_QRC qml/qml.qrc)
|
qt5_add_resources(QML_QRC qml/qml.qrc)
|
||||||
|
|
||||||
add_executable(light_client ClientDataModel.cpp ClientDataModel.hpp main.cpp ${QML_QRC} ${QML})
|
add_executable(light_client ClientDataModel.cpp ClientDataModel.hpp main.cpp ${QML_QRC} ${QML})
|
||||||
|
if (CMAKE_VERSION VERSION_LESS 3.0)
|
||||||
add_dependencies(light_client gen_qrc)
|
add_dependencies(light_client gen_qrc)
|
||||||
|
endif()
|
||||||
|
|
||||||
target_link_libraries(light_client PRIVATE Qt5::Core Qt5::Widgets Qt5::Quick graphene_chain graphene_utilities fc graphene_app )
|
target_link_libraries(light_client PRIVATE Qt5::Core Qt5::Widgets Qt5::Quick graphene_chain graphene_utilities fc graphene_app )
|
||||||
|
|
|
||||||
|
|
@ -134,13 +134,14 @@ Asset* ChainDataModel::getAsset(QString symbol)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Account* ChainDataModel::getAccount(qint64 id)
|
Account* ChainDataModel::getAccount(ObjectId id)
|
||||||
{
|
{
|
||||||
auto& by_id_idx = m_accounts.get<::by_id>();
|
auto& by_id_idx = m_accounts.get<::by_id>();
|
||||||
auto itr = by_id_idx.find(id);
|
auto itr = by_id_idx.find(id);
|
||||||
if( itr == by_id_idx.end() )
|
if( itr == by_id_idx.end() )
|
||||||
{
|
{
|
||||||
auto tmp = new Account;
|
auto tmp = new Account;
|
||||||
|
QQmlEngine::setObjectOwnership(tmp, QQmlEngine::CppOwnership);
|
||||||
tmp->id = id; --m_account_query_num;
|
tmp->id = id; --m_account_query_num;
|
||||||
tmp->name = QString::number( --m_account_query_num);
|
tmp->name = QString::number( --m_account_query_num);
|
||||||
auto result = m_accounts.insert( tmp );
|
auto result = m_accounts.insert( tmp );
|
||||||
|
|
@ -191,6 +192,7 @@ Account* ChainDataModel::getAccount(QString name)
|
||||||
if( itr == by_name_idx.end() )
|
if( itr == by_name_idx.end() )
|
||||||
{
|
{
|
||||||
auto tmp = new Account;
|
auto tmp = new Account;
|
||||||
|
QQmlEngine::setObjectOwnership(tmp, QQmlEngine::CppOwnership);
|
||||||
tmp->id = --m_account_query_num;
|
tmp->id = --m_account_query_num;
|
||||||
tmp->name = name;
|
tmp->name = name;
|
||||||
auto result = m_accounts.insert( tmp );
|
auto result = m_accounts.insert( tmp );
|
||||||
|
|
@ -218,7 +220,7 @@ Account* ChainDataModel::getAccount(QString name)
|
||||||
{
|
{
|
||||||
by_symbol_idx.modify( itr,
|
by_symbol_idx.modify( itr,
|
||||||
[=]( Account* a ){
|
[=]( Account* a ){
|
||||||
a->setProperty("id", result.front()->id.instance() );
|
a->setProperty("id", ObjectId(result.front()->id.instance()));
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -278,6 +280,7 @@ void GrapheneApplication::start( QString apiurl, QString user, QString pass )
|
||||||
m_client = std::make_shared<fc::http::websocket_client>();
|
m_client = std::make_shared<fc::http::websocket_client>();
|
||||||
ilog( "connecting...${s}", ("s",apiurl.toStdString()) );
|
ilog( "connecting...${s}", ("s",apiurl.toStdString()) );
|
||||||
auto con = m_client->connect( apiurl.toStdString() );
|
auto con = m_client->connect( apiurl.toStdString() );
|
||||||
|
m_connectionClosed = con->closed.connect([this]{queueExecute([this]{setIsConnected(false);});});
|
||||||
auto apic = std::make_shared<fc::rpc::websocket_api_connection>(*con);
|
auto apic = std::make_shared<fc::rpc::websocket_api_connection>(*con);
|
||||||
auto remote_api = apic->get_remote_api< login_api >(1);
|
auto remote_api = apic->get_remote_api< login_api >(1);
|
||||||
auto db_api = apic->get_remote_api< database_api >(0);
|
auto db_api = apic->get_remote_api< database_api >(0);
|
||||||
|
|
|
||||||
|
|
@ -11,25 +11,38 @@
|
||||||
#include <fc/thread/thread.hpp>
|
#include <fc/thread/thread.hpp>
|
||||||
#include <graphene/app/api.hpp>
|
#include <graphene/app/api.hpp>
|
||||||
|
|
||||||
|
#include <QtQml>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QQmlListProperty>
|
#include <QQmlListProperty>
|
||||||
|
|
||||||
using boost::multi_index_container;
|
using boost::multi_index_container;
|
||||||
using namespace boost::multi_index;
|
using namespace boost::multi_index;
|
||||||
|
|
||||||
|
using ObjectId = qint64;
|
||||||
|
Q_DECLARE_METATYPE(ObjectId)
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(std::function<void()>)
|
Q_DECLARE_METATYPE(std::function<void()>)
|
||||||
|
|
||||||
class GrapheneObject : public QObject
|
class GrapheneObject : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_PROPERTY(qint64 id MEMBER id NOTIFY idChanged)
|
Q_PROPERTY(ObjectId id MEMBER id NOTIFY idChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
qint64 id;
|
ObjectId id;
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void idChanged();
|
void idChanged();
|
||||||
};
|
};
|
||||||
|
class Crypto {
|
||||||
|
Q_GADGET
|
||||||
|
|
||||||
|
public:
|
||||||
|
Q_INVOKABLE QString sha256(QByteArray data) {
|
||||||
|
return QCryptographicHash::hash(data, QCryptographicHash::Sha256).toHex();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
QML_DECLARE_TYPE(Crypto)
|
||||||
|
|
||||||
|
|
||||||
class Asset : public GrapheneObject {
|
class Asset : public GrapheneObject {
|
||||||
|
|
@ -77,7 +90,6 @@ class Account : public GrapheneObject {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
const QString& getName()const { return name; }
|
const QString& getName()const { return name; }
|
||||||
|
|
||||||
QQmlListProperty<Balance> balances();
|
QQmlListProperty<Balance> balances();
|
||||||
|
|
||||||
QString name;
|
QString name;
|
||||||
|
|
@ -90,19 +102,16 @@ struct by_account_name;
|
||||||
typedef multi_index_container<
|
typedef multi_index_container<
|
||||||
Account*,
|
Account*,
|
||||||
indexed_by<
|
indexed_by<
|
||||||
hashed_unique< tag<by_id>, member<GrapheneObject, qint64, &GrapheneObject::id > >,
|
hashed_unique< tag<by_id>, member<GrapheneObject, ObjectId, &GrapheneObject::id > >,
|
||||||
ordered_unique< tag<by_account_name>, member<Account, QString, &Account::name> >
|
ordered_unique< tag<by_account_name>, member<Account, QString, &Account::name> >
|
||||||
>
|
>
|
||||||
> account_multi_index_type;
|
> account_multi_index_type;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ChainDataModel : public QObject {
|
class ChainDataModel : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Q_INVOKABLE Account* getAccount(qint64 id);
|
Q_INVOKABLE Account* getAccount(ObjectId id);
|
||||||
Q_INVOKABLE Account* getAccount(QString name);
|
Q_INVOKABLE Account* getAccount(QString name);
|
||||||
Q_INVOKABLE Asset* getAsset(qint64 id);
|
Q_INVOKABLE Asset* getAsset(qint64 id);
|
||||||
Q_INVOKABLE Asset* getAsset(QString symbol);
|
Q_INVOKABLE Asset* getAsset(QString symbol);
|
||||||
|
|
@ -121,15 +130,11 @@ private:
|
||||||
std::string m_api_url;
|
std::string m_api_url;
|
||||||
fc::api<graphene::app::database_api> m_db_api;
|
fc::api<graphene::app::database_api> m_db_api;
|
||||||
|
|
||||||
qint64 m_account_query_num = -1;
|
ObjectId m_account_query_num = -1;
|
||||||
account_multi_index_type m_accounts;
|
account_multi_index_type m_accounts;
|
||||||
asset_multi_index_type m_assets;
|
asset_multi_index_type m_assets;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class GrapheneApplication : public QObject {
|
class GrapheneApplication : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
|
@ -141,6 +146,8 @@ class GrapheneApplication : public QObject {
|
||||||
ChainDataModel* m_model = nullptr;
|
ChainDataModel* m_model = nullptr;
|
||||||
bool m_isConnected = false;
|
bool m_isConnected = false;
|
||||||
|
|
||||||
|
boost::signals2::scoped_connection m_connectionClosed;
|
||||||
|
|
||||||
std::shared_ptr<fc::http::websocket_client> m_client;
|
std::shared_ptr<fc::http::websocket_client> m_client;
|
||||||
fc::future<void> m_done;
|
fc::future<void> m_done;
|
||||||
|
|
||||||
|
|
|
||||||
11
programs/light_client/README.md
Normal file
11
programs/light_client/README.md
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
== Graphene Client GUI ==
|
||||||
|
|
||||||
|
This is a Qt-based native GUI client for Graphene blockchains.
|
||||||
|
|
||||||
|
To build this GUI, run cmake with -DBUILD_QT_GUI=ON
|
||||||
|
|
||||||
|
This GUI depends on Qt 5.5 or later. If you do not have Qt 5.5 installed
|
||||||
|
in the canonical location on your OS (or if your OS does not have a
|
||||||
|
canonical location for libraries), you can specify the Qt path by running
|
||||||
|
cmake with -DCMAKE_PREFIX_PATH=/path/to/Qt/5.5/gcc_64 as appropriate for
|
||||||
|
your environment.
|
||||||
|
|
@ -13,6 +13,7 @@ int main(int argc, char *argv[])
|
||||||
app.setOrganizationName("Cryptonomex, Inc.");
|
app.setOrganizationName("Cryptonomex, Inc.");
|
||||||
|
|
||||||
qRegisterMetaType<std::function<void()>>();
|
qRegisterMetaType<std::function<void()>>();
|
||||||
|
qRegisterMetaType<ObjectId>();
|
||||||
|
|
||||||
qmlRegisterType<Asset>("Graphene.Client", 0, 1, "Asset");
|
qmlRegisterType<Asset>("Graphene.Client", 0, 1, "Asset");
|
||||||
qmlRegisterType<Balance>("Graphene.Client", 0, 1, "Balance");
|
qmlRegisterType<Balance>("Graphene.Client", 0, 1, "Balance");
|
||||||
|
|
@ -21,11 +22,9 @@ int main(int argc, char *argv[])
|
||||||
qmlRegisterType<GrapheneApplication>("Graphene.Client", 0, 1, "GrapheneApplication");
|
qmlRegisterType<GrapheneApplication>("Graphene.Client", 0, 1, "GrapheneApplication");
|
||||||
|
|
||||||
QQmlApplicationEngine engine;
|
QQmlApplicationEngine engine;
|
||||||
/*
|
|
||||||
QVariant crypto;
|
QVariant crypto;
|
||||||
crypto.setValue(Crypto());
|
crypto.setValue(Crypto());
|
||||||
engine.rootContext()->setContextProperty("Crypto", crypto);
|
engine.rootContext()->setContextProperty("Crypto", crypto);
|
||||||
*/
|
|
||||||
#ifdef NDEBUG
|
#ifdef NDEBUG
|
||||||
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
|
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
|
||||||
#else
|
#else
|
||||||
|
|
|
||||||
72
programs/light_client/qml/AccountPicker.qml
Normal file
72
programs/light_client/qml/AccountPicker.qml
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
import QtQuick 2.5
|
||||||
|
import QtQuick.Controls 1.4
|
||||||
|
import QtQuick.Dialogs 1.2
|
||||||
|
import QtQuick.Layouts 1.2
|
||||||
|
|
||||||
|
import Graphene.Client 0.1
|
||||||
|
|
||||||
|
import "."
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
property Account account
|
||||||
|
|
||||||
|
property alias placeholderText: accountNameField.placeholderText
|
||||||
|
|
||||||
|
function setFocus() {
|
||||||
|
accountNameField.forceActiveFocus()
|
||||||
|
}
|
||||||
|
|
||||||
|
Identicon {
|
||||||
|
name: accountNameField.text
|
||||||
|
width: Scaling.cm(2)
|
||||||
|
height: Scaling.cm(2)
|
||||||
|
}
|
||||||
|
Column {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
TextField {
|
||||||
|
id: accountNameField
|
||||||
|
|
||||||
|
width: parent.width
|
||||||
|
onEditingFinished: accountDetails.update(text)
|
||||||
|
}
|
||||||
|
Label {
|
||||||
|
id: accountDetails
|
||||||
|
function update(name) {
|
||||||
|
if (!name)
|
||||||
|
{
|
||||||
|
text = ""
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
account = app.model.getAccount(name)
|
||||||
|
if (account == null)
|
||||||
|
text = qsTr("Error fetching account.")
|
||||||
|
else
|
||||||
|
text = Qt.binding(function() {
|
||||||
|
if (account == null)
|
||||||
|
return qsTr("Account does not exist.")
|
||||||
|
return qsTr("Account ID: %1").arg(account.id < 0? qsTr("Loading...")
|
||||||
|
: account.id)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on text {
|
||||||
|
SequentialAnimation {
|
||||||
|
PropertyAnimation {
|
||||||
|
target: accountDetails
|
||||||
|
property: "opacity"
|
||||||
|
from: 1; to: 0
|
||||||
|
duration: 100
|
||||||
|
}
|
||||||
|
PropertyAction { target: accountDetails; property: "text" }
|
||||||
|
PropertyAnimation {
|
||||||
|
target: accountDetails
|
||||||
|
property: "opacity"
|
||||||
|
from: 0; to: 1
|
||||||
|
duration: 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -22,13 +22,28 @@ Rectangle {
|
||||||
function showForm(formType, params, closedCallback) {
|
function showForm(formType, params, closedCallback) {
|
||||||
if (formType.status === Component.Error)
|
if (formType.status === Component.Error)
|
||||||
console.log(formType.errorString())
|
console.log(formType.errorString())
|
||||||
|
if (!params instanceof Object)
|
||||||
|
params = {app: app}
|
||||||
|
else
|
||||||
|
params.app = app
|
||||||
|
|
||||||
formContainer.data = [formType.createObject(formContainer, params)]
|
var form = formType.createObject(formContainer, params)
|
||||||
|
formContainer.data = [form]
|
||||||
|
form.finished.connect(function(){state = "HIDDEN"})
|
||||||
if (closedCallback instanceof Function)
|
if (closedCallback instanceof Function)
|
||||||
internal.callback = closedCallback
|
internal.callback = closedCallback
|
||||||
state = "SHOWN"
|
state = "SHOWN"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FocusScope {
|
||||||
|
id: scope
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
// Do not let focus leave this scope while form is open
|
||||||
|
onFocusChanged: if (enabled && !focus) forceActiveFocus()
|
||||||
|
|
||||||
|
Keys.onEscapePressed: greySheet.state = "HIDDEN"
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: mouseTrap
|
id: mouseTrap
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
@ -39,6 +54,7 @@ Rectangle {
|
||||||
acceptedButtons: Qt.AllButtons
|
acceptedButtons: Qt.AllButtons
|
||||||
}
|
}
|
||||||
MouseArea {
|
MouseArea {
|
||||||
|
// This mouse area blocks clicks inside the form from reaching the mouseTrap
|
||||||
anchors.fill: formContainer
|
anchors.fill: formContainer
|
||||||
acceptedButtons: Qt.AllButtons
|
acceptedButtons: Qt.AllButtons
|
||||||
onClicked: mouse.accepted = true
|
onClicked: mouse.accepted = true
|
||||||
|
|
@ -49,6 +65,7 @@ Rectangle {
|
||||||
width: parent.width / 2
|
width: parent.width / 2
|
||||||
height: parent.height / 2
|
height: parent.height / 2
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
states: [
|
states: [
|
||||||
State {
|
State {
|
||||||
|
|
|
||||||
39
programs/light_client/qml/Identicon.qml
Normal file
39
programs/light_client/qml/Identicon.qml
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
import QtQuick 2.5
|
||||||
|
|
||||||
|
import "jdenticon/jdenticon-1.0.1.min.js" as Jdenticon
|
||||||
|
|
||||||
|
Canvas {
|
||||||
|
id: identicon
|
||||||
|
contextType: "2d"
|
||||||
|
|
||||||
|
property var name
|
||||||
|
onNameChanged: requestPaint()
|
||||||
|
|
||||||
|
onPaint: {
|
||||||
|
if (name)
|
||||||
|
Jdenticon.draw(identicon, name)
|
||||||
|
else {
|
||||||
|
var context = identicon.context
|
||||||
|
context.reset()
|
||||||
|
var draw_circle = function(context, x, y, radius) {
|
||||||
|
context.beginPath()
|
||||||
|
context.arc(x, y, radius, 0, 2 * Math.PI, false)
|
||||||
|
context.fillStyle = "rgba(0, 0, 0, 0.1)"
|
||||||
|
context.fill()
|
||||||
|
}
|
||||||
|
var size = Math.min(identicon.height, identicon.width)
|
||||||
|
var centerX = size / 2
|
||||||
|
var centerY = size / 2
|
||||||
|
var radius = size/15
|
||||||
|
draw_circle(context, centerX, centerY, radius)
|
||||||
|
draw_circle(context, 2*radius, 2*radius, radius)
|
||||||
|
draw_circle(context, centerX, 2*radius, radius)
|
||||||
|
draw_circle(context, size - 2*radius, 2*radius, radius)
|
||||||
|
draw_circle(context, size - 2*radius, centerY, radius)
|
||||||
|
draw_circle(context, size - 2*radius, size - 2*radius, radius)
|
||||||
|
draw_circle(context, centerX, size - 2*radius, radius)
|
||||||
|
draw_circle(context, 2*radius, size - 2*radius, radius)
|
||||||
|
draw_circle(context, 2*radius, centerY, radius)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,57 +3,61 @@ import QtQuick.Controls 1.4
|
||||||
import QtQuick.Dialogs 1.2
|
import QtQuick.Dialogs 1.2
|
||||||
import QtQuick.Layouts 1.2
|
import QtQuick.Layouts 1.2
|
||||||
|
|
||||||
|
import Graphene.Client 0.1
|
||||||
|
|
||||||
import "."
|
import "."
|
||||||
import "jdenticon/jdenticon-1.0.1.min.js" as Jdenticon
|
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
|
property alias senderAccount: senderPicker.account
|
||||||
|
property alias receiverAccount: recipientPicker.account
|
||||||
|
|
||||||
|
property GrapheneApplication app
|
||||||
|
signal finished
|
||||||
|
|
||||||
Component.onCompleted: console.log("Made a transfer form")
|
Component.onCompleted: console.log("Made a transfer form")
|
||||||
Component.onDestruction: console.log("Destroyed a transfer form")
|
Component.onDestruction: console.log("Destroyed a transfer form")
|
||||||
|
|
||||||
Column {
|
ColumnLayout {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
|
width: parent.width - Scaling.cm(2)
|
||||||
|
spacing: Scaling.cm(1)
|
||||||
|
|
||||||
|
AccountPicker {
|
||||||
|
id: senderPicker
|
||||||
|
width: parent.width
|
||||||
|
Component.onCompleted: setFocus()
|
||||||
|
placeholderText: qsTr("Sender")
|
||||||
|
}
|
||||||
|
AccountPicker {
|
||||||
|
id: recipientPicker
|
||||||
|
width: parent.width
|
||||||
|
placeholderText: qsTr("Recipient")
|
||||||
|
layoutDirection: Qt.RightToLeft
|
||||||
|
}
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Canvas {
|
width: parent.width
|
||||||
id: identicon
|
SpinBox {
|
||||||
width: Scaling.cm(2)
|
Layout.preferredWidth: Scaling.cm(4)
|
||||||
height: Scaling.cm(2)
|
Layout.minimumWidth: Scaling.cm(1.5)
|
||||||
contextType: "2d"
|
enabled: senderPicker.account
|
||||||
|
minimumValue: 0
|
||||||
onPaint: {
|
maximumValue: Number.POSITIVE_INFINITY
|
||||||
if (nameField.text)
|
|
||||||
Jdenticon.draw(identicon, nameField.text)
|
|
||||||
else {
|
|
||||||
var context = identicon.context
|
|
||||||
context.reset()
|
|
||||||
var draw_circle = function(context, x, y, radius) {
|
|
||||||
context.beginPath()
|
|
||||||
context.arc(x, y, radius, 0, 2 * Math.PI, false)
|
|
||||||
context.fillStyle = "rgba(0, 0, 0, 0.1)"
|
|
||||||
context.fill()
|
|
||||||
}
|
}
|
||||||
var size = Math.min(identicon.height, identicon.width)
|
ComboBox {
|
||||||
var centerX = size / 2
|
Layout.minimumWidth: Scaling.cm(3)
|
||||||
var centerY = size / 2
|
enabled: senderPicker.account
|
||||||
var radius = size/15
|
model: ["CORE", "USD", "GOLD"]
|
||||||
draw_circle(context, centerX, centerY, radius)
|
|
||||||
draw_circle(context, 2*radius, 2*radius, radius)
|
|
||||||
draw_circle(context, centerX, 2*radius, radius)
|
|
||||||
draw_circle(context, size - 2*radius, 2*radius, radius)
|
|
||||||
draw_circle(context, size - 2*radius, centerY, radius)
|
|
||||||
draw_circle(context, size - 2*radius, size - 2*radius, radius)
|
|
||||||
draw_circle(context, centerX, size - 2*radius, radius)
|
|
||||||
draw_circle(context, 2*radius, size - 2*radius, radius)
|
|
||||||
draw_circle(context, 2*radius, centerY, radius)
|
|
||||||
}
|
}
|
||||||
|
Item { Layout.fillWidth: true }
|
||||||
|
Button {
|
||||||
|
text: qsTr("Cancel")
|
||||||
|
onClicked: finished()
|
||||||
}
|
}
|
||||||
}
|
Button {
|
||||||
TextField {
|
text: qsTr("Transfer")
|
||||||
id: nameField
|
enabled: senderPicker.account
|
||||||
Layout.fillWidth: true
|
|
||||||
onTextChanged: identicon.requestPaint()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,10 +14,6 @@ ApplicationWindow {
|
||||||
height: 480
|
height: 480
|
||||||
title: qsTr("Hello World")
|
title: qsTr("Hello World")
|
||||||
|
|
||||||
Component.onCompleted: {
|
|
||||||
app.start("ws://localhost:8090", "user", "pass")
|
|
||||||
}
|
|
||||||
|
|
||||||
menuBar: MenuBar {
|
menuBar: MenuBar {
|
||||||
Menu {
|
Menu {
|
||||||
title: qsTr("File")
|
title: qsTr("File")
|
||||||
|
|
@ -28,12 +24,26 @@ ApplicationWindow {
|
||||||
MenuItem {
|
MenuItem {
|
||||||
text: qsTr("Exit")
|
text: qsTr("Exit")
|
||||||
onTriggered: Qt.quit();
|
onTriggered: Qt.quit();
|
||||||
|
shortcut: "Ctrl+Q"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
statusBar: StatusBar {
|
||||||
|
Label {
|
||||||
|
anchors.right: parent.right
|
||||||
|
text: app.isConnected? qsTr("Connected") : qsTr("Disconnected")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
GrapheneApplication {
|
GrapheneApplication {
|
||||||
id: app
|
id: app
|
||||||
|
}
|
||||||
|
Timer {
|
||||||
|
running: !app.isConnected
|
||||||
|
interval: 5000
|
||||||
|
repeat: true
|
||||||
|
onTriggered: app.start("ws://localhost:8090", "user", "pass")
|
||||||
|
triggeredOnStart: true
|
||||||
}
|
}
|
||||||
Settings {
|
Settings {
|
||||||
id: appSettings
|
id: appSettings
|
||||||
|
|
@ -46,6 +56,8 @@ ApplicationWindow {
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
|
enabled: app.isConnected
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
text: "Transfer"
|
text: "Transfer"
|
||||||
onClicked: formBox.showForm(Qt.createComponent("TransferForm.qml"), {},
|
onClicked: formBox.showForm(Qt.createComponent("TransferForm.qml"), {},
|
||||||
|
|
@ -142,8 +154,6 @@ ApplicationWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FormBox {
|
FormBox {
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,10 @@
|
||||||
<file>main.qml</file>
|
<file>main.qml</file>
|
||||||
<file>TransferForm.qml</file>
|
<file>TransferForm.qml</file>
|
||||||
<file>FormBox.qml</file>
|
<file>FormBox.qml</file>
|
||||||
<file>jdenticon/jdenticon-1.0.1.min.js</file>
|
|
||||||
<file>Scaling.qml</file>
|
<file>Scaling.qml</file>
|
||||||
|
<file>Identicon.qml</file>
|
||||||
|
<file>AccountPicker.qml</file>
|
||||||
|
<file>jdenticon/jdenticon-1.0.1.min.js</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue