explorer: Add explorer subproject with javafx screens for cash and transactions
3
.idea/modules.xml
generated
@ -18,6 +18,9 @@
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/experimental/experimental.iml" filepath="$PROJECT_DIR$/.idea/modules/experimental/experimental.iml" group="experimental" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/experimental/experimental_main.iml" filepath="$PROJECT_DIR$/.idea/modules/experimental/experimental_main.iml" group="experimental" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/experimental/experimental_test.iml" filepath="$PROJECT_DIR$/.idea/modules/experimental/experimental_test.iml" group="experimental" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/explorer/explorer.iml" filepath="$PROJECT_DIR$/.idea/modules/explorer/explorer.iml" group="explorer" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/explorer/explorer_main.iml" filepath="$PROJECT_DIR$/.idea/modules/explorer/explorer_main.iml" group="explorer" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/explorer/explorer_test.iml" filepath="$PROJECT_DIR$/.idea/modules/explorer/explorer_test.iml" group="explorer" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/contracts/isolated/isolated.iml" filepath="$PROJECT_DIR$/.idea/modules/contracts/isolated/isolated.iml" group="contracts/isolated" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/contracts/isolated/isolated_main.iml" filepath="$PROJECT_DIR$/.idea/modules/contracts/isolated/isolated_main.iml" group="contracts/isolated" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/contracts/isolated/isolated_test.iml" filepath="$PROJECT_DIR$/.idea/modules/contracts/isolated/isolated_test.iml" group="contracts/isolated" />
|
||||
|
74
explorer/build.gradle
Normal file
@ -0,0 +1,74 @@
|
||||
group 'com.r3corda'
|
||||
version '1.0-SNAPSHOT'
|
||||
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.0.3'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven {
|
||||
url 'https://dl.bintray.com/kotlin/exposed'
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'java'
|
||||
apply plugin: 'kotlin'
|
||||
apply plugin: 'application'
|
||||
|
||||
sourceCompatibility = 1.8
|
||||
|
||||
applicationDefaultJvmArgs = ["-javaagent:${rootProject.configurations.quasar.singleFile}"]
|
||||
mainClassName = 'com.r3corda.explorer.Main'
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
resources {
|
||||
srcDir "../config/dev"
|
||||
}
|
||||
}
|
||||
test {
|
||||
resources {
|
||||
srcDir "../config/test"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
mavenLocal()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||
testCompile group: 'junit', name: 'junit', version: '4.11'
|
||||
|
||||
// TornadoFX: A lightweight Kotlin framework for working with JavaFX UI's.
|
||||
compile 'no.tornado:tornadofx:1.5.1'
|
||||
|
||||
// Corda Core: Data structures and basic types needed to work with Corda.
|
||||
compile project(':core')
|
||||
compile project(':client')
|
||||
compile project(':node')
|
||||
compile project(':contracts')
|
||||
|
||||
// FontAwesomeFX: The "FontAwesome" icon library.
|
||||
compile 'de.jensd:fontawesomefx-fontawesome:4.6.1-2'
|
||||
|
||||
// ReactFX: Functional reactive UI programming.
|
||||
compile 'org.reactfx:reactfx:2.0-M5'
|
||||
compile 'org.fxmisc.easybind:easybind:1.0.3'
|
||||
|
||||
// JFXtras: useful widgets including a calendar control.
|
||||
compile 'org.jfxtras:jfxtras-agenda:8.0-r5'
|
||||
compile 'org.jfxtras:jfxtras-font-roboto:8.0-r5'
|
||||
}
|
74
explorer/src/main/kotlin/com/r3corda/explorer/Main.kt
Normal file
@ -0,0 +1,74 @@
|
||||
package com.r3corda.explorer
|
||||
|
||||
import com.r3corda.client.WalletMonitorClient
|
||||
import com.r3corda.client.mock.*
|
||||
import com.r3corda.client.model.*
|
||||
import com.r3corda.core.contracts.*
|
||||
import com.r3corda.node.driver.PortAllocation
|
||||
import com.r3corda.node.services.monitor.ServiceToClientEvent
|
||||
import com.r3corda.node.driver.driver
|
||||
import com.r3corda.node.driver.startClient
|
||||
import com.r3corda.node.services.transactions.SimpleNotaryService
|
||||
import javafx.stage.Stage
|
||||
import org.reactfx.EventSource
|
||||
import tornadofx.App
|
||||
import java.util.*
|
||||
|
||||
class Main : App() {
|
||||
override val primaryView = MainWindow::class
|
||||
val aliceOutStream: org.reactfx.EventSink<ClientToServiceCommand> by sink(WalletMonitorModel::clientToService)
|
||||
|
||||
override fun start(stage: Stage) {
|
||||
|
||||
Thread.setDefaultUncaughtExceptionHandler { thread, throwable ->
|
||||
throwable.printStackTrace()
|
||||
System.exit(1)
|
||||
}
|
||||
|
||||
super.start(stage)
|
||||
|
||||
// start the driver on another thread
|
||||
Thread({
|
||||
|
||||
val portAllocation = PortAllocation.Incremental(20000)
|
||||
driver(portAllocation = portAllocation) {
|
||||
|
||||
val aliceNodeFuture = startNode("Alice")
|
||||
val bobNodeFuture = startNode("Bob")
|
||||
val notaryNodeFuture = startNode("Notary", advertisedServices = setOf(SimpleNotaryService.Type))
|
||||
|
||||
val aliceNode = aliceNodeFuture.get()
|
||||
val bobNode = bobNodeFuture.get()
|
||||
val notaryNode = notaryNodeFuture.get()
|
||||
|
||||
val aliceClient = startClient(aliceNode).get()
|
||||
|
||||
Models.get<WalletMonitorModel>(Main::class).register(aliceClient, aliceNode)
|
||||
|
||||
val bobInStream = EventSource<ServiceToClientEvent>()
|
||||
val bobOutStream = EventSource<ClientToServiceCommand>()
|
||||
|
||||
val bobClient = startClient(bobNode).get()
|
||||
val bobMonitorClient = WalletMonitorClient(bobClient, bobNode, bobOutStream, bobInStream)
|
||||
assert(bobMonitorClient.register().get())
|
||||
|
||||
for (i in 0 .. 10000) {
|
||||
Thread.sleep(500)
|
||||
|
||||
val eventGenerator = EventGenerator(
|
||||
parties = listOf(aliceNode.identity, bobNode.identity),
|
||||
notary = notaryNode.identity
|
||||
)
|
||||
|
||||
eventGenerator.clientToServiceCommandGenerator.combine(Generator.oneOf(listOf(aliceOutStream, bobOutStream))) {
|
||||
command, stream -> stream.push(command)
|
||||
}.generate(Random())
|
||||
}
|
||||
|
||||
waitForAllNodesToFinish()
|
||||
}
|
||||
|
||||
}).start()
|
||||
}
|
||||
}
|
||||
|
26
explorer/src/main/kotlin/com/r3corda/explorer/MainWindow.kt
Normal file
@ -0,0 +1,26 @@
|
||||
package com.r3corda.explorer
|
||||
|
||||
import com.r3corda.explorer.views.TopLevel
|
||||
import de.jensd.fx.glyphs.fontawesome.utils.FontAwesomeIconFactory
|
||||
import jfxtras.resources.JFXtrasFontRoboto
|
||||
import tornadofx.*
|
||||
|
||||
/**
|
||||
* The root view embeds the [Shell] and provides support for the status bar, and modal dialogs.
|
||||
*/
|
||||
class MainWindow : View() {
|
||||
private val toplevel: TopLevel by inject()
|
||||
override val root = toplevel.root
|
||||
|
||||
init {
|
||||
// Do this first before creating the notification bar, so it can autosize itself properly.
|
||||
loadFontsAndStyles()
|
||||
}
|
||||
|
||||
private fun loadFontsAndStyles() {
|
||||
JFXtrasFontRoboto.loadAll()
|
||||
importStylesheet("/com/r3corda/explorer/css/wallet.css")
|
||||
FontAwesomeIconFactory.get() // Force initialisation.
|
||||
root.styleClass += "root"
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
package com.r3corda.explorer.formatters
|
||||
|
||||
import com.r3corda.core.contracts.Amount
|
||||
import java.text.DecimalFormat
|
||||
import java.util.*
|
||||
|
||||
class AmountFormatter {
|
||||
|
||||
companion object {
|
||||
fun currency(formatter: Formatter<Amount<Currency>>) = object : Formatter<Amount<Currency>> {
|
||||
override fun format(value: Amount<Currency>) =
|
||||
"${value.token.currencyCode} ${formatter.format(value)}"
|
||||
}
|
||||
|
||||
val comma = object : Formatter<Amount<Currency>> {
|
||||
override fun format(value: Amount<Currency>) =
|
||||
NumberFormatter.doubleComma.format(value.quantity / 100.0)
|
||||
}
|
||||
|
||||
private data class KmbRange(val fromLog: Double, val toLog: Double, val letter: String, val divider: (Double) -> Double)
|
||||
private val kmbRanges = listOf(
|
||||
KmbRange(Double.NEGATIVE_INFINITY, Math.log(1000.0), "", { value -> value }),
|
||||
KmbRange(Math.log(1000.0), Math.log(1000000.0), "k", { value -> value / 1000.0 }),
|
||||
KmbRange(Math.log(1000000.0), Math.log(1000000000.0), "m", { value -> value / 1000000.0 }),
|
||||
KmbRange(Math.log(1000000000.0), Double.POSITIVE_INFINITY, "b", { value -> value / 1000000000.0 })
|
||||
)
|
||||
|
||||
fun kmb(formatter: Formatter<Double>) = object : Formatter<Amount<Currency>> {
|
||||
override fun format(value: Amount<Currency>): String {
|
||||
val displayAmount = value.quantity / 100.0
|
||||
val logarithm = Math.log(displayAmount)
|
||||
val rangeIndex = kmbRanges.binarySearch(
|
||||
comparison = { range ->
|
||||
if (logarithm < range.fromLog) {
|
||||
1
|
||||
} else if (logarithm < range.toLog) {
|
||||
0
|
||||
} else {
|
||||
-1
|
||||
}
|
||||
}
|
||||
)
|
||||
val kmbRange = kmbRanges[rangeIndex]
|
||||
return "${formatter.format(kmbRange.divider(displayAmount))}${kmbRange.letter}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
package com.r3corda.explorer.formatters
|
||||
|
||||
import com.r3corda.core.contracts.Amount
|
||||
import java.text.DecimalFormat
|
||||
import java.util.*
|
||||
|
||||
class CurrencyFormatter {
|
||||
|
||||
companion object {
|
||||
private val commaFormatter = DecimalFormat("#,###.00")
|
||||
|
||||
fun currency(formatter: Formatter<Amount<Currency>>) = object : Formatter<Amount<Currency>> {
|
||||
override fun format(value: Amount<Currency>) =
|
||||
"${value.token.currencyCode} ${formatter.format(value)}"
|
||||
}
|
||||
|
||||
val comma = object : Formatter<Amount<Currency>> {
|
||||
override fun format(value: Amount<Currency>) =
|
||||
commaFormatter.format(value.quantity / 100.0)
|
||||
}
|
||||
|
||||
data class KmbRange(val fromLog: Double, val toLog: Double, val letter: String, val divider: (Double) -> Double)
|
||||
val kmbRanges = listOf(
|
||||
KmbRange(Double.NEGATIVE_INFINITY, Math.log(1000.0), "", { value -> value }),
|
||||
KmbRange(Math.log(1000.0), Math.log(1000000.0), "k", { value -> value / 1000.0 }),
|
||||
KmbRange(Math.log(1000000.0), Math.log(1000000000.0), "m", { value -> value / 1000000.0 }),
|
||||
KmbRange(Math.log(1000000000.0), Double.POSITIVE_INFINITY, "b", { value -> value / 1000000000.0 })
|
||||
)
|
||||
|
||||
val kmbComma = object : Formatter<Amount<Currency>> {
|
||||
override fun format(value: Amount<Currency>): String {
|
||||
val displayAmount = value.quantity / 100.0
|
||||
val logarithm = Math.log(displayAmount)
|
||||
val rangeIndex = kmbRanges.binarySearch(
|
||||
comparison = { range ->
|
||||
if (logarithm < range.fromLog) {
|
||||
1
|
||||
} else if (logarithm < range.toLog) {
|
||||
0
|
||||
} else {
|
||||
-1
|
||||
}
|
||||
}
|
||||
)
|
||||
val kmbRange = kmbRanges[rangeIndex]
|
||||
return "${commaFormatter.format(kmbRange.divider(displayAmount))}${kmbRange.letter}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
package com.r3corda.explorer.formatters
|
||||
|
||||
|
||||
interface Formatter<T> {
|
||||
fun format(value: T): String
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package com.r3corda.explorer.formatters
|
||||
|
||||
import java.text.DecimalFormat
|
||||
|
||||
|
||||
class NumberFormatter {
|
||||
companion object {
|
||||
private val doubleCommaFormatter = DecimalFormat("#,###.00")
|
||||
private val integralCommaFormatter = DecimalFormat("#,###")
|
||||
|
||||
val doubleComma = object : Formatter<Double> {
|
||||
override fun format(value: Double) =
|
||||
doubleCommaFormatter.format(value)
|
||||
}
|
||||
|
||||
val longComma = object : Formatter<Long> {
|
||||
override fun format(value: Long) =
|
||||
integralCommaFormatter.format(value)
|
||||
}
|
||||
val intComma = object : Formatter<Int> {
|
||||
override fun format(value: Int) =
|
||||
integralCommaFormatter.format(value)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package com.r3corda.explorer.model
|
||||
|
||||
import com.r3corda.core.contracts.Amount
|
||||
import com.r3corda.client.fxutils.AmountBindings
|
||||
import com.r3corda.client.model.ExchangeRate
|
||||
import com.r3corda.client.model.ExchangeRateModel
|
||||
import com.r3corda.client.model.observableValue
|
||||
import javafx.beans.value.ObservableValue
|
||||
import org.fxmisc.easybind.EasyBind
|
||||
import java.util.*
|
||||
|
||||
class ReportingCurrencyModel {
|
||||
private val exchangeRate: ObservableValue<ExchangeRate> by observableValue(ExchangeRateModel::exchangeRate)
|
||||
val reportingCurrency: ObservableValue<Currency> by observableValue(SettingsModel::reportingCurrency)
|
||||
/**
|
||||
* This stream provides a stream of exchange() functions that updates when either the reporting currency or the
|
||||
* exchange rates change
|
||||
*/
|
||||
val reportingExchange: ObservableValue<Pair<Currency, (Amount<Currency>) -> Amount<Currency>>> =
|
||||
EasyBind.map(AmountBindings.exchange(reportingCurrency, exchangeRate)) { Pair(it.first) { amount: Amount<Currency> ->
|
||||
Amount(it.second(amount), it.first)
|
||||
}}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package com.r3corda.explorer.model
|
||||
|
||||
import com.r3corda.core.contracts.USD
|
||||
import javafx.beans.property.SimpleObjectProperty
|
||||
import java.util.*
|
||||
|
||||
class SettingsModel {
|
||||
|
||||
val reportingCurrency: SimpleObjectProperty<Currency> = SimpleObjectProperty(USD)
|
||||
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package com.r3corda.explorer.model
|
||||
|
||||
import javafx.beans.property.SimpleObjectProperty
|
||||
|
||||
enum class SelectedView {
|
||||
Home,
|
||||
Cash,
|
||||
Transaction
|
||||
}
|
||||
|
||||
class TopLevelModel {
|
||||
val selectedView = SimpleObjectProperty<SelectedView>(SelectedView.Home)
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
package com.r3corda.explorer.ui
|
||||
|
||||
import com.r3corda.explorer.formatters.Formatter
|
||||
import javafx.beans.binding.Bindings
|
||||
import javafx.beans.value.ObservableValue
|
||||
import javafx.scene.control.TableCell
|
||||
import javafx.scene.control.TableColumn
|
||||
import javafx.scene.control.TableView
|
||||
import javafx.util.Callback
|
||||
import org.fxmisc.easybind.EasyBind
|
||||
|
||||
fun <S> TableView<S>.setColumnPrefWidthPolicy(
|
||||
getColumnWidth: (tableWidthWithoutPaddingAndBorder: Number, column: TableColumn<S, *>) -> Number
|
||||
) {
|
||||
val tableWidthWithoutPaddingAndBorder = Bindings.createDoubleBinding({
|
||||
val padding = padding
|
||||
val borderInsets = border?.insets
|
||||
width -
|
||||
(if (padding != null) padding.left + padding.right else 0.0) -
|
||||
(if (borderInsets != null) borderInsets.left + borderInsets.right else 0.0)
|
||||
}, arrayOf(columns, widthProperty(), paddingProperty(), borderProperty()))
|
||||
|
||||
columns.forEach {
|
||||
it.setPrefWidthPolicy(tableWidthWithoutPaddingAndBorder, getColumnWidth)
|
||||
}
|
||||
}
|
||||
|
||||
private fun <S> TableColumn<S, *>.setPrefWidthPolicy(
|
||||
widthWithoutPaddingAndBorder: ObservableValue<Number>,
|
||||
getColumnWidth: (tableWidthWithoutPaddingAndBorder: Number, column: TableColumn<S, *>) -> Number
|
||||
) {
|
||||
prefWidthProperty().bind(EasyBind.map(widthWithoutPaddingAndBorder) {
|
||||
getColumnWidth(it, this)
|
||||
})
|
||||
}
|
||||
|
||||
fun <S, T> Formatter<T>.toTableCellFactory() = Callback<TableColumn<S, T?>, TableCell<S, T?>> {
|
||||
object : TableCell<S, T?>() {
|
||||
override fun updateItem(value: T?, empty: Boolean) {
|
||||
super.updateItem(value, empty)
|
||||
text = if (value == null || empty) {
|
||||
""
|
||||
} else {
|
||||
format(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,49 @@
|
||||
package com.r3corda.explorer.ui
|
||||
|
||||
import com.r3corda.explorer.formatters.Formatter
|
||||
import javafx.beans.binding.Bindings
|
||||
import javafx.beans.value.ObservableValue
|
||||
import javafx.scene.control.TreeTableCell
|
||||
import javafx.scene.control.TreeTableColumn
|
||||
import javafx.scene.control.TreeTableView
|
||||
import javafx.util.Callback
|
||||
import org.fxmisc.easybind.EasyBind
|
||||
|
||||
|
||||
fun <S> TreeTableView<S>.setColumnPrefWidthPolicy(
|
||||
getColumnWidth: (tableWidthWithoutPaddingAndBorder: Number, column: TreeTableColumn<S, *>) -> Number
|
||||
) {
|
||||
val tableWidthWithoutPaddingAndBorder = Bindings.createDoubleBinding({
|
||||
val padding = padding
|
||||
val borderInsets = border?.insets
|
||||
width -
|
||||
(if (padding != null) padding.left + padding.right else 0.0) -
|
||||
(if (borderInsets != null) borderInsets.left + borderInsets.right else 0.0)
|
||||
}, arrayOf(columns, widthProperty(), paddingProperty(), borderProperty()))
|
||||
|
||||
columns.forEach {
|
||||
it.setPrefWidthPolicy(tableWidthWithoutPaddingAndBorder, getColumnWidth)
|
||||
}
|
||||
}
|
||||
|
||||
private fun <S> TreeTableColumn<S, *>.setPrefWidthPolicy(
|
||||
widthWithoutPaddingAndBorder: ObservableValue<Number>,
|
||||
getColumnWidth: (tableWidthWithoutPaddingAndBorder: Number, column: TreeTableColumn<S, *>) -> Number
|
||||
) {
|
||||
prefWidthProperty().bind(EasyBind.map(widthWithoutPaddingAndBorder) {
|
||||
getColumnWidth(it, this)
|
||||
})
|
||||
}
|
||||
|
||||
fun <S, T> Formatter<T>.toTreeTableCellFactory() = Callback<TreeTableColumn<S, T?>, TreeTableCell<S, T?>> {
|
||||
object : TreeTableCell<S, T?>() {
|
||||
override fun updateItem(value: T?, empty: Boolean) {
|
||||
super.updateItem(value, empty)
|
||||
text = if (value == null || empty) {
|
||||
""
|
||||
} else {
|
||||
format(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,362 @@
|
||||
package com.r3corda.explorer.views
|
||||
|
||||
import com.r3corda.client.fxutils.AggregatedList
|
||||
import com.r3corda.client.fxutils.ChosenList
|
||||
import com.r3corda.client.model.ContractStateModel
|
||||
import com.r3corda.client.model.observableList
|
||||
import com.r3corda.client.model.observableValue
|
||||
import com.r3corda.contracts.asset.Cash
|
||||
import com.r3corda.core.contracts.Amount
|
||||
import com.r3corda.core.contracts.StateAndRef
|
||||
import com.r3corda.core.contracts.withoutIssuer
|
||||
import com.r3corda.core.crypto.Party
|
||||
import com.r3corda.explorer.formatters.AmountFormatter
|
||||
import com.r3corda.explorer.formatters.NumberFormatter
|
||||
import com.r3corda.explorer.model.ReportingCurrencyModel
|
||||
import com.r3corda.explorer.model.SettingsModel
|
||||
import com.r3corda.explorer.ui.setColumnPrefWidthPolicy
|
||||
import com.r3corda.explorer.ui.toTreeTableCellFactory
|
||||
import javafx.beans.binding.Bindings
|
||||
import javafx.beans.property.ReadOnlyObjectWrapper
|
||||
import javafx.beans.property.ReadOnlyStringWrapper
|
||||
import javafx.beans.property.SimpleObjectProperty
|
||||
import javafx.beans.value.ObservableValue
|
||||
import javafx.collections.FXCollections
|
||||
import javafx.collections.ObservableList
|
||||
import javafx.collections.transformation.FilteredList
|
||||
import javafx.scene.Node
|
||||
import javafx.scene.control.*
|
||||
import javafx.scene.image.ImageView
|
||||
import javafx.scene.input.MouseButton
|
||||
import javafx.scene.input.MouseEvent
|
||||
import javafx.scene.layout.HBox
|
||||
import javafx.scene.layout.VBox
|
||||
import kotlinx.support.jdk8.collections.stream
|
||||
import org.fxmisc.easybind.EasyBind
|
||||
import tornadofx.UIComponent
|
||||
import tornadofx.View
|
||||
import tornadofx.selectedItem
|
||||
import java.time.LocalDateTime
|
||||
import java.util.*
|
||||
import java.util.function.Predicate
|
||||
import java.util.stream.Collectors
|
||||
|
||||
sealed class FilterCriteria {
|
||||
abstract fun matches(string: String): Boolean
|
||||
|
||||
object All : FilterCriteria() {
|
||||
override fun matches(string: String) = true
|
||||
}
|
||||
|
||||
class FilterString(val filterString: String) : FilterCriteria() {
|
||||
override fun matches(string: String) = string.contains(filterString)
|
||||
}
|
||||
}
|
||||
|
||||
class CashViewer : View() {
|
||||
// Inject UI elements
|
||||
override val root: SplitPane by fxml()
|
||||
|
||||
val topSplitPane: SplitPane by fxid("TopSplitPane")
|
||||
// Left pane
|
||||
val leftPane: VBox by fxid("LeftPane")
|
||||
val searchCriteriaTextField: TextField by fxid("SearchCriteriaTextField")
|
||||
val searchCancelImageView: ImageView by fxid("SearchCancelImageView")
|
||||
val totalMatchingLabel: Label by fxid("TotalMatchingLabel")
|
||||
val cashViewerTable: TreeTableView<ViewerNode> by fxid("CashViewerTable")
|
||||
val cashViewerTableIssuerCurrency: TreeTableColumn<ViewerNode, String> by fxid("CashViewerTableIssuerCurrency")
|
||||
val cashViewerTableLocalCurrency: TreeTableColumn<ViewerNode, Amount<Currency>?> by fxid("CashViewerTableLocalCurrency")
|
||||
val cashViewerTableEquiv: TreeTableColumn<ViewerNode, Amount<Currency>?> by fxid("CashViewerTableEquiv")
|
||||
|
||||
// Right pane
|
||||
val rightPane: VBox by fxid("RightPane")
|
||||
val totalPositionsLabel: Label by fxid("TotalPositionsLabel")
|
||||
val equivSumLabel: Label by fxid("EquivSumLabel")
|
||||
val cashStatesList: ListView<StateRow> by fxid("CashStatesList")
|
||||
|
||||
// Inject observables
|
||||
val cashStates by observableList(ContractStateModel::cashStates)
|
||||
val reportingCurrency: ObservableValue<Currency> by observableValue(SettingsModel::reportingCurrency)
|
||||
val reportingExchange: ObservableValue<Pair<Currency, (Amount<Currency>) -> Amount<Currency>>>
|
||||
by observableValue(ReportingCurrencyModel::reportingExchange)
|
||||
|
||||
sealed class ViewerNode {
|
||||
object Root : ViewerNode()
|
||||
class IssuerNode(
|
||||
val issuer: Party,
|
||||
val sumEquivAmount: ObservableValue<Amount<Currency>>,
|
||||
val states: ObservableList<StateAndRef<Cash.State>>
|
||||
) : ViewerNode()
|
||||
class CurrencyNode(
|
||||
val amount: ObservableValue<Amount<Currency>>,
|
||||
val equivAmount: ObservableValue<Amount<Currency>>,
|
||||
val states: ObservableList<StateAndRef<Cash.State>>
|
||||
) : ViewerNode()
|
||||
}
|
||||
|
||||
private val filterCriteria = EasyBind.map(searchCriteriaTextField.textProperty()) { text ->
|
||||
if (text == "") {
|
||||
FilterCriteria.All
|
||||
} else {
|
||||
FilterCriteria.FilterString(text)
|
||||
}
|
||||
}
|
||||
|
||||
private val issueFilteredCashStates = FilteredList(cashStates).apply {
|
||||
predicateProperty().bind(EasyBind.map(filterCriteria) { filterCriteria ->
|
||||
Predicate<StateAndRef<Cash.State>> {
|
||||
filterCriteria.matches(it.state.data.amount.token.issuer.party.toString())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private val currencyFilteredCashStates = FilteredList(cashStates).apply {
|
||||
predicateProperty().bind(EasyBind.map(filterCriteria) { filterCriteria ->
|
||||
Predicate<StateAndRef<Cash.State>> {
|
||||
filterCriteria.matches(it.state.data.amount.token.product.toString())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
enum class FilterMethod {
|
||||
Issue,
|
||||
Currency
|
||||
}
|
||||
|
||||
private val filterMethod = Bindings.createObjectBinding({
|
||||
if (issueFilteredCashStates.size > currencyFilteredCashStates.size) {
|
||||
FilterMethod.Issue
|
||||
} else {
|
||||
FilterMethod.Currency
|
||||
}
|
||||
}, arrayOf(filterCriteria))
|
||||
|
||||
private val filteredCashStates = ChosenList<StateAndRef<Cash.State>>(EasyBind.map(filterMethod) {
|
||||
when (it) {
|
||||
FilterMethod.Issue -> issueFilteredCashStates
|
||||
FilterMethod.Currency -> currencyFilteredCashStates
|
||||
null -> issueFilteredCashStates
|
||||
}
|
||||
})
|
||||
|
||||
val cashViewerIssueNodes: ObservableList<TreeItem<ViewerNode.IssuerNode>> =
|
||||
AggregatedList(filteredCashStates, { it.state.data.amount.token.issuer.party }) { issuer, memberStates ->
|
||||
val currencyNodes = AggregatedList(memberStates, { it.state.data.amount.token.product }) { currency, memberStates ->
|
||||
val sumAmount = EasyBind.map(
|
||||
Bindings.createLongBinding({
|
||||
memberStates.stream().collect(Collectors.summingLong { it.state.data.amount.quantity })
|
||||
}, arrayOf(memberStates))
|
||||
) { sum -> Amount(sum.toLong(), currency) }
|
||||
|
||||
val equivSumAmount = EasyBind.combine(sumAmount, reportingExchange) { sum, exchange ->
|
||||
exchange.second(sum)
|
||||
}
|
||||
TreeItem(ViewerNode.CurrencyNode(sumAmount, equivSumAmount, memberStates))
|
||||
}
|
||||
|
||||
val equivSumAmount =
|
||||
EasyBind.combine(
|
||||
EasyBind.combine(EasyBind.map(currencyNodes, { it.value.equivAmount })) {
|
||||
it.collect(Collectors.summingLong(Amount<Currency>::quantity))
|
||||
},
|
||||
reportingCurrency
|
||||
) { sum, currency ->
|
||||
Amount(sum.toLong(), currency)
|
||||
}
|
||||
|
||||
val treeItem = TreeItem(ViewerNode.IssuerNode(issuer, equivSumAmount, memberStates))
|
||||
treeItem.isExpanded = true
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
Bindings.bindContent(treeItem.children as ObservableList<TreeItem<out ViewerNode>>, currencyNodes)
|
||||
treeItem
|
||||
}
|
||||
|
||||
sealed class ViewerNodeSelection {
|
||||
object None : ViewerNodeSelection()
|
||||
class Selected(val node: ViewerNode) : ViewerNodeSelection()
|
||||
}
|
||||
|
||||
val selectedViewerNode = SimpleObjectProperty<ViewerNodeSelection>(ViewerNodeSelection.None)
|
||||
|
||||
data class StateRow (
|
||||
val originated: LocalDateTime,
|
||||
val stateAndRef: StateAndRef<Cash.State>
|
||||
)
|
||||
|
||||
inner class StateRowGraphic(
|
||||
val stateRow: StateRow
|
||||
) : UIComponent() {
|
||||
override val root: HBox by fxml("CashStateViewer.fxml")
|
||||
|
||||
val equivLabel: Label by fxid("EquivLabel")
|
||||
val stateIdValueLabel: Label by fxid("StateIdValueLabel")
|
||||
val issuerValueLabel: Label by fxid("IssuerValueLabel")
|
||||
val originatedValueLabel: Label by fxid("OriginatedValueLabel")
|
||||
val amountValueLabel: Label by fxid("AmountValueLabel")
|
||||
val equivValueLabel: Label by fxid("EquivValueLabel")
|
||||
|
||||
val equivAmount: ObservableValue<Amount<Currency>> = EasyBind.map(reportingExchange) {
|
||||
it.second(stateRow.stateAndRef.state.data.amount.withoutIssuer())
|
||||
}
|
||||
|
||||
init {
|
||||
val amountNoIssuer = stateRow.stateAndRef.state.data.amount.withoutIssuer()
|
||||
val amountFormatter = AmountFormatter.currency(AmountFormatter.comma)
|
||||
val equivFormatter = AmountFormatter.comma
|
||||
|
||||
equivLabel.textProperty().bind(EasyBind.map(equivAmount) { it.token.currencyCode.toString() })
|
||||
stateIdValueLabel.text = stateRow.stateAndRef.ref.toString()
|
||||
issuerValueLabel.text = stateRow.stateAndRef.state.data.amount.token.issuer.toString()
|
||||
originatedValueLabel.text = stateRow.originated.toString()
|
||||
amountValueLabel.text = amountFormatter.format(amountNoIssuer)
|
||||
equivValueLabel.textProperty().bind(EasyBind.map(equivAmount) { equivFormatter.format(it) })
|
||||
}
|
||||
}
|
||||
|
||||
private val noSelectionStates = FXCollections.observableArrayList<StateAndRef<Cash.State>>()
|
||||
private val selectedViewerNodeStates = ChosenList(EasyBind.map(selectedViewerNode) { selection ->
|
||||
when (selection) {
|
||||
CashViewer.ViewerNodeSelection.None -> noSelectionStates
|
||||
is CashViewer.ViewerNodeSelection.Selected ->
|
||||
when (selection.node) {
|
||||
CashViewer.ViewerNode.Root -> noSelectionStates
|
||||
is CashViewer.ViewerNode.IssuerNode -> selection.node.states
|
||||
is CashViewer.ViewerNode.CurrencyNode -> selection.node.states
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
private val noSelectionSumEquiv = EasyBind.map(reportingCurrency) { Amount(0, it) }
|
||||
private val selectedViewerNodeSumEquiv = EasyBind.monadic(selectedViewerNode).flatMap { selection ->
|
||||
when (selection) {
|
||||
ViewerNodeSelection.None -> noSelectionSumEquiv
|
||||
is ViewerNodeSelection.Selected ->
|
||||
when (selection.node) {
|
||||
ViewerNode.Root -> noSelectionSumEquiv
|
||||
is ViewerNode.IssuerNode -> selection.node.sumEquivAmount
|
||||
is ViewerNode.CurrencyNode -> selection.node.equivAmount
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val stateRows = EasyBind.map(selectedViewerNodeStates) {
|
||||
StateRow(LocalDateTime.now(), it)
|
||||
}
|
||||
|
||||
private val onlyLeftPaneShown = FXCollections.observableArrayList<Node>(leftPane)
|
||||
private val bothPanesShown = FXCollections.observableArrayList<Node>(leftPane, rightPane)
|
||||
private val panesShown = ChosenList(EasyBind.map(selectedViewerNode) {
|
||||
when (it) {
|
||||
CashViewer.ViewerNodeSelection.None -> onlyLeftPaneShown
|
||||
is CashViewer.ViewerNodeSelection.Selected -> bothPanesShown
|
||||
}
|
||||
})
|
||||
|
||||
// Wire up UI
|
||||
init {
|
||||
|
||||
searchCancelImageView.setOnMouseClicked { event: MouseEvent ->
|
||||
if (event.button == MouseButton.PRIMARY) {
|
||||
searchCriteriaTextField.text = ""
|
||||
}
|
||||
}
|
||||
|
||||
cashViewerTable.setOnMouseClicked { event: MouseEvent ->
|
||||
if (event.button == MouseButton.PRIMARY) {
|
||||
val selected = cashViewerTable.selectedItem
|
||||
selectedViewerNode.set(
|
||||
if (selected == null) {
|
||||
ViewerNodeSelection.None
|
||||
} else {
|
||||
ViewerNodeSelection.Selected(selected)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Bindings.bindContent(topSplitPane.items, panesShown)
|
||||
|
||||
rightPane.visibleProperty().bind(EasyBind.map(selectedViewerNode) {
|
||||
it != ViewerNodeSelection.None
|
||||
})
|
||||
|
||||
totalPositionsLabel.textProperty().bind(Bindings.createStringBinding({
|
||||
val positionsCount = selectedViewerNodeStates.size
|
||||
val plural = if (positionsCount == 1) "" else "s"
|
||||
"Total $positionsCount position$plural"
|
||||
}, arrayOf(selectedViewerNodeStates)))
|
||||
|
||||
val equivSumLabelFormatter = AmountFormatter.currency(AmountFormatter.kmb(NumberFormatter.doubleComma))
|
||||
equivSumLabel.textProperty().bind(EasyBind.map(selectedViewerNodeSumEquiv) {
|
||||
equivSumLabelFormatter.format(it)
|
||||
})
|
||||
|
||||
Bindings.bindContent(cashStatesList.items, stateRows)
|
||||
|
||||
cashStatesList.setCellFactory {
|
||||
object : ListCell<StateRow>() {
|
||||
init {
|
||||
text = null
|
||||
}
|
||||
override fun updateItem(value: StateRow?, empty: Boolean) {
|
||||
super.updateItem(value, empty)
|
||||
graphic = if (value != null && !empty) {
|
||||
StateRowGraphic(value).root
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val cellFactory = AmountFormatter.comma.toTreeTableCellFactory<ViewerNode, Amount<Currency>>()
|
||||
|
||||
cashViewerTable.setColumnPrefWidthPolicy { tableWidthWithoutPaddingAndBorder, column ->
|
||||
Math.floor(tableWidthWithoutPaddingAndBorder.toDouble() / cashViewerTable.columns.size).toInt()
|
||||
}
|
||||
|
||||
cashViewerTableIssuerCurrency.setCellValueFactory {
|
||||
val node = it.value.value
|
||||
when (node) {
|
||||
ViewerNode.Root -> ReadOnlyStringWrapper("")
|
||||
is ViewerNode.IssuerNode -> ReadOnlyStringWrapper(node.issuer.toString())
|
||||
is ViewerNode.CurrencyNode -> EasyBind.map(node.amount) { it.token.toString() }
|
||||
}
|
||||
}
|
||||
cashViewerTableLocalCurrency.setCellValueFactory {
|
||||
val node = it.value.value
|
||||
when (node) {
|
||||
ViewerNode.Root -> ReadOnlyObjectWrapper(null)
|
||||
is ViewerNode.IssuerNode -> ReadOnlyObjectWrapper(null)
|
||||
is ViewerNode.CurrencyNode -> EasyBind.map(node.amount) { it }
|
||||
}
|
||||
}
|
||||
cashViewerTableLocalCurrency.cellFactory = cellFactory
|
||||
cashViewerTableLocalCurrency.isSortable = false
|
||||
cashViewerTableEquiv.setCellValueFactory {
|
||||
val node = it.value.value
|
||||
when (node) {
|
||||
ViewerNode.Root -> ReadOnlyObjectWrapper(null)
|
||||
is ViewerNode.IssuerNode -> EasyBind.map(node.sumEquivAmount) { it }
|
||||
is ViewerNode.CurrencyNode -> EasyBind.map(node.equivAmount) { it }
|
||||
}
|
||||
}
|
||||
cashViewerTableEquiv.cellFactory = cellFactory
|
||||
cashViewerTableEquiv.textProperty().bind(EasyBind.map(reportingCurrency) { "$it Equiv" })
|
||||
|
||||
cashViewerTable.root = TreeItem(ViewerNode.Root)
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
Bindings.bindContent(cashViewerTable.root.children as ObservableList<TreeItem<out ViewerNode>>, cashViewerIssueNodes)
|
||||
|
||||
cashViewerTable.root.isExpanded = true
|
||||
cashViewerTable.isShowRoot = false
|
||||
|
||||
totalMatchingLabel.textProperty().bind(EasyBind.map(
|
||||
Bindings.createIntegerBinding({ cashViewerIssueNodes.size }, arrayOf(cashViewerIssueNodes))
|
||||
) {
|
||||
val plural = if (it == 1) "" else "s"
|
||||
"Total $it matching issuer$plural"
|
||||
})
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
package com.r3corda.explorer.views
|
||||
|
||||
import com.r3corda.client.model.observableValue
|
||||
import com.r3corda.explorer.model.SelectedView
|
||||
import com.r3corda.explorer.model.TopLevelModel
|
||||
import javafx.beans.value.ObservableValue
|
||||
import javafx.scene.control.Button
|
||||
import javafx.scene.control.Label
|
||||
import javafx.scene.image.Image
|
||||
import javafx.scene.image.ImageView
|
||||
import javafx.scene.layout.VBox
|
||||
import org.fxmisc.easybind.EasyBind
|
||||
import tornadofx.View
|
||||
|
||||
class Header : View() {
|
||||
override val root: VBox by fxml()
|
||||
|
||||
private val sectionIcon: ImageView by fxid("SectionIcon")
|
||||
private val sectionIconContainer: VBox by fxid("SectionIconContainer")
|
||||
private val sectionLabel: Label by fxid("SectionLabel")
|
||||
private val debugNextButton: Button by fxid("DebugNextButton")
|
||||
private val debugGoStopButton: Button by fxid("DebugGoStopButton")
|
||||
|
||||
private val selectedView: ObservableValue<SelectedView> by observableValue(TopLevelModel::selectedView)
|
||||
|
||||
private val homeImage = Image("/com/r3corda/explorer/images/home.png")
|
||||
private val cashImage = Image("/com/r3corda/explorer/images/cash.png")
|
||||
private val transactionImage = Image("/com/r3corda/explorer/images/tx.png")
|
||||
|
||||
init {
|
||||
sectionLabel.textProperty().bind(EasyBind.map(selectedView) {
|
||||
when (it) {
|
||||
SelectedView.Home -> "Home"
|
||||
SelectedView.Cash -> "Cash"
|
||||
SelectedView.Transaction -> "Transaction"
|
||||
null -> "Home"
|
||||
}
|
||||
})
|
||||
|
||||
sectionIcon.imageProperty().bind(EasyBind.map(selectedView) {
|
||||
when (it) {
|
||||
SelectedView.Home -> homeImage
|
||||
SelectedView.Cash -> cashImage
|
||||
SelectedView.Transaction -> transactionImage
|
||||
null -> homeImage
|
||||
}
|
||||
})
|
||||
|
||||
// JavaFX bugs and doesn't invalidate the wrapping Box's height if the icon fit height is first set to
|
||||
// unbounded (0.0) - which is what the label's height is initially, so we set it to 1.0 instead
|
||||
val secionLabelHeightNonZero = EasyBind.map(sectionLabel.heightProperty()) {
|
||||
if (it == 0.0) {
|
||||
1.0
|
||||
} else {
|
||||
it.toDouble()
|
||||
}
|
||||
}
|
||||
|
||||
sectionIconContainer.minWidthProperty().bind(secionLabelHeightNonZero)
|
||||
sectionIcon.fitWidthProperty().bind(secionLabelHeightNonZero)
|
||||
sectionIcon.fitHeightProperty().bind(sectionIcon.fitWidthProperty())
|
||||
}
|
||||
}
|
70
explorer/src/main/kotlin/com/r3corda/explorer/views/Home.kt
Normal file
@ -0,0 +1,70 @@
|
||||
package com.r3corda.explorer.views
|
||||
|
||||
import com.r3corda.client.fxutils.AmountBindings
|
||||
import com.r3corda.client.model.*
|
||||
import com.r3corda.contracts.asset.Cash
|
||||
import com.r3corda.core.contracts.StateAndRef
|
||||
import com.r3corda.core.contracts.withoutIssuer
|
||||
import com.r3corda.explorer.formatters.AmountFormatter
|
||||
import com.r3corda.explorer.formatters.NumberFormatter
|
||||
import com.r3corda.explorer.model.SelectedView
|
||||
import com.r3corda.explorer.model.SettingsModel
|
||||
import com.r3corda.explorer.model.TopLevelModel
|
||||
import javafx.beans.binding.Bindings
|
||||
import javafx.beans.value.ObservableValue
|
||||
import javafx.beans.value.WritableValue
|
||||
import javafx.collections.ObservableList
|
||||
import javafx.scene.control.Label
|
||||
import javafx.scene.control.TitledPane
|
||||
import javafx.scene.input.MouseButton
|
||||
import javafx.scene.layout.TilePane
|
||||
import org.fxmisc.easybind.EasyBind
|
||||
import tornadofx.View
|
||||
import java.util.*
|
||||
|
||||
|
||||
class Home : View() {
|
||||
override val root: TilePane by fxml()
|
||||
|
||||
private val ourCashPane: TitledPane by fxid("OurCashPane")
|
||||
private val ourCashLabel: Label by fxid("OurCashLabel")
|
||||
|
||||
private val ourTransactionsPane: TitledPane by fxid("OurTransactionsPane")
|
||||
private val ourTransactionsLabel: Label by fxid("OurTransactionsLabel")
|
||||
|
||||
private val selectedView: WritableValue<SelectedView> by writableValue(TopLevelModel::selectedView)
|
||||
private val cashStates: ObservableList<StateAndRef<Cash.State>> by observableList(ContractStateModel::cashStates)
|
||||
private val transactionCreateStates: ObservableList<out TransactionCreateState>
|
||||
by observableListReadOnly(TransactionCreateStateModel::transactionCreateStates)
|
||||
private val reportingCurrency: ObservableValue<Currency> by observableValue(SettingsModel::reportingCurrency)
|
||||
private val exchangeRate: ObservableValue<ExchangeRate> by observableValue(ExchangeRateModel::exchangeRate)
|
||||
|
||||
private val sumAmount = AmountBindings.sumAmountExchange(
|
||||
EasyBind.map(cashStates) { it.state.data.amount.withoutIssuer() },
|
||||
reportingCurrency,
|
||||
exchangeRate
|
||||
)
|
||||
|
||||
init {
|
||||
val formatter = AmountFormatter.currency(AmountFormatter.kmb(NumberFormatter.doubleComma))
|
||||
|
||||
ourCashLabel.textProperty().bind(EasyBind.map(sumAmount) { formatter.format(it) })
|
||||
ourCashPane.setOnMouseClicked { clickEvent ->
|
||||
if (clickEvent.button == MouseButton.PRIMARY) {
|
||||
selectedView.value = SelectedView.Cash
|
||||
}
|
||||
}
|
||||
|
||||
ourTransactionsLabel.textProperty().bind(
|
||||
Bindings.createStringBinding({
|
||||
NumberFormatter.intComma.format(transactionCreateStates.size)
|
||||
}, arrayOf(transactionCreateStates))
|
||||
)
|
||||
ourTransactionsPane.setOnMouseClicked { clickEvent ->
|
||||
if (clickEvent.button == MouseButton.PRIMARY) {
|
||||
selectedView.value = SelectedView.Transaction
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
package com.r3corda.explorer.views
|
||||
|
||||
import com.r3corda.client.model.objectProperty
|
||||
import com.r3corda.explorer.model.SelectedView
|
||||
import com.r3corda.explorer.model.TopLevelModel
|
||||
import javafx.beans.property.ObjectProperty
|
||||
import javafx.scene.input.KeyCode
|
||||
import javafx.scene.input.KeyEvent
|
||||
import javafx.scene.layout.BorderPane
|
||||
import javafx.scene.layout.Priority
|
||||
import javafx.scene.layout.VBox
|
||||
import org.fxmisc.easybind.EasyBind
|
||||
import tornadofx.View
|
||||
|
||||
class TopLevel : View() {
|
||||
override val root: VBox by fxml()
|
||||
val selection: BorderPane by fxid("SelectionBorderPane")
|
||||
|
||||
private val header: Header by inject()
|
||||
private val home: Home by inject()
|
||||
private val cash: CashViewer by inject()
|
||||
private val transaction: TransactionViewer by inject()
|
||||
|
||||
// Note: this is weirdly very important, as it forces the initialisation of Views. Therefore this is the entry
|
||||
// point to the top level observable/stream wiring! Any events sent before this init may be lost!
|
||||
private val homeRoot = home.root
|
||||
private val cashRoot = cash.root
|
||||
private val transactionRoot = transaction.root
|
||||
|
||||
private fun getView(selection: SelectedView) = when (selection) {
|
||||
SelectedView.Home -> homeRoot
|
||||
SelectedView.Cash -> cashRoot
|
||||
SelectedView.Transaction -> transactionRoot
|
||||
}
|
||||
val selectedView: ObjectProperty<SelectedView> by objectProperty(TopLevelModel::selectedView)
|
||||
|
||||
init {
|
||||
VBox.setVgrow(selection, Priority.ALWAYS)
|
||||
selection.centerProperty().bind(EasyBind.map(selectedView) { getView(it) })
|
||||
|
||||
primaryStage.addEventHandler(KeyEvent.KEY_RELEASED) { keyEvent ->
|
||||
if (keyEvent.code == KeyCode.ESCAPE) {
|
||||
selectedView.value = SelectedView.Home
|
||||
}
|
||||
}
|
||||
|
||||
root.children.add(0, header.root)
|
||||
}
|
||||
}
|
@ -0,0 +1,150 @@
|
||||
package com.r3corda.explorer.views
|
||||
|
||||
import com.r3corda.client.model.*
|
||||
import com.r3corda.contracts.asset.Cash
|
||||
import com.r3corda.core.contracts.Amount
|
||||
import com.r3corda.core.contracts.CommandData
|
||||
import com.r3corda.core.contracts.SignedTransaction
|
||||
import com.r3corda.core.contracts.withoutIssuer
|
||||
import com.r3corda.explorer.formatters.AmountFormatter
|
||||
import com.r3corda.explorer.model.ReportingCurrencyModel
|
||||
import com.r3corda.explorer.ui.setColumnPrefWidthPolicy
|
||||
import com.r3corda.explorer.ui.toTableCellFactory
|
||||
import javafx.beans.binding.Bindings
|
||||
import javafx.beans.value.ObservableValue
|
||||
import javafx.collections.ObservableList
|
||||
import javafx.geometry.Insets
|
||||
import javafx.scene.control.Label
|
||||
import javafx.scene.control.TableCell
|
||||
import javafx.scene.control.TableColumn
|
||||
import javafx.scene.control.TableView
|
||||
import javafx.scene.layout.Background
|
||||
import javafx.scene.layout.BackgroundFill
|
||||
import javafx.scene.layout.CornerRadii
|
||||
import javafx.scene.layout.VBox
|
||||
import javafx.scene.paint.Color
|
||||
import org.fxmisc.easybind.EasyBind
|
||||
import tornadofx.View
|
||||
import java.time.Instant
|
||||
import java.util.*
|
||||
|
||||
class TransactionViewer: View() {
|
||||
override val root: VBox by fxml()
|
||||
|
||||
private val transactionViewTable: TableView<ViewerNode> by fxid("TransactionViewTable")
|
||||
private val transactionViewTransactionId: TableColumn<ViewerNode, String> by fxid("TransactionViewTransactionId")
|
||||
private val transactionViewOriginator: TableColumn<ViewerNode, String> by fxid("TransactionViewOriginator")
|
||||
private val transactionViewTransactionStatus: TableColumn<ViewerNode, Pair<TransactionCreateStatus?, ProtocolStatus?>> by fxid("TransactionViewTransactionStatus")
|
||||
private val transactionViewStatusUpdated: TableColumn<ViewerNode, Instant> by fxid("TransactionViewStatusUpdated")
|
||||
private val transactionViewCommandTypes: TableColumn<ViewerNode, String> by fxid("TransactionViewCommandTypes")
|
||||
private val transactionViewTotalValueEquiv: TableColumn<ViewerNode, Amount<Currency>> by fxid("TransactionViewTotalValueEquiv")
|
||||
|
||||
private val transactionCreateStates: ObservableList<out TransactionCreateState>
|
||||
by observableListReadOnly(TransactionCreateStateModel::transactionCreateStates)
|
||||
private val reportingExchange: ObservableValue<Pair<Currency, (Amount<Currency>) -> Amount<Currency>>>
|
||||
by observableValue(ReportingCurrencyModel::reportingExchange)
|
||||
|
||||
data class ViewerNode(
|
||||
val transactionId: ObservableValue<Pair<Long?, UUID?>>,
|
||||
val originator: ObservableValue<String>,
|
||||
val transactionStatus: ObservableValue<Pair<TransactionCreateStatus?, ProtocolStatus?>>,
|
||||
val statusUpdated: ObservableValue<Instant>,
|
||||
val commandTypes: ObservableValue<Collection<Class<CommandData>>>,
|
||||
val viewTotalValueEquiv: ObservableValue<Amount<Currency>?>,
|
||||
val transaction: ObservableValue<SignedTransaction?>
|
||||
)
|
||||
|
||||
private val viewerNodes = EasyBind.map(transactionCreateStates) {
|
||||
ViewerNode(
|
||||
transactionId = EasyBind.combine(it.fiberId, it.uuid) { fiberId, uuid -> Pair(fiberId, uuid) },
|
||||
originator = EasyBind.map(it.uuid) { uuid ->
|
||||
if (uuid == null) {
|
||||
"Someone"
|
||||
} else {
|
||||
"Us"
|
||||
}
|
||||
},
|
||||
transactionStatus = EasyBind.combine(it.status, it.protocolStatus) { status, protocolStatus ->
|
||||
Pair(status, protocolStatus)
|
||||
},
|
||||
statusUpdated = it.lastUpdate,
|
||||
commandTypes = EasyBind.map(it.transaction) {
|
||||
val commands = mutableSetOf<Class<CommandData>>()
|
||||
it?.tx?.commands?.forEach {
|
||||
commands.add(it.value.javaClass)
|
||||
}
|
||||
commands
|
||||
},
|
||||
viewTotalValueEquiv = EasyBind.combine(reportingExchange, it.transaction) { exchange, transaction ->
|
||||
transaction?.let { calculateTotalEquiv(exchange.first, exchange.second, transaction) }
|
||||
},
|
||||
transaction = it.transaction
|
||||
)
|
||||
}
|
||||
|
||||
private fun calculateTotalEquiv(
|
||||
reportingCurrency: Currency,
|
||||
exchange: (Amount<Currency>) -> Amount<Currency>,
|
||||
transaction: SignedTransaction): Amount<Currency> {
|
||||
return transaction.tx.outputs.map { it.data }.filterIsInstance<Cash.State>().fold(
|
||||
initial = Amount(0, reportingCurrency),
|
||||
operation = { sum, cashState -> sum + exchange(cashState.amount.withoutIssuer()) }
|
||||
)
|
||||
}
|
||||
|
||||
init {
|
||||
Bindings.bindContent(transactionViewTable.items, viewerNodes)
|
||||
|
||||
transactionViewTable.setColumnPrefWidthPolicy { tableWidthWithoutPaddingAndBorder, column ->
|
||||
Math.floor(tableWidthWithoutPaddingAndBorder.toDouble() / transactionViewTable.columns.size).toInt()
|
||||
}
|
||||
|
||||
transactionViewTransactionId.setCellValueFactory {
|
||||
EasyBind.map(it.value.transactionId) {
|
||||
val (fiberId, uuid) = it
|
||||
if (fiberId == null && uuid == null) {
|
||||
"???"
|
||||
} else {
|
||||
(uuid?.toString() ?: "") + (fiberId?.let { "[$it]" } ?: "")
|
||||
}
|
||||
}
|
||||
}
|
||||
transactionViewOriginator.setCellValueFactory { it.value.originator }
|
||||
transactionViewTransactionStatus.setCellValueFactory { it.value.transactionStatus }
|
||||
transactionViewTransactionStatus.setCellFactory {
|
||||
object : TableCell<ViewerNode, Pair<TransactionCreateStatus?, ProtocolStatus?>>() {
|
||||
val label = Label()
|
||||
override fun updateItem(
|
||||
value: Pair<TransactionCreateStatus?, ProtocolStatus?>?,
|
||||
empty: Boolean
|
||||
) {
|
||||
super.updateItem(value, empty)
|
||||
if (value == null || empty) {
|
||||
graphic = null
|
||||
text = null
|
||||
} else {
|
||||
graphic = label
|
||||
val backgroundFill = when (value.first) {
|
||||
is TransactionCreateStatus.Started -> BackgroundFill(Color.TRANSPARENT, CornerRadii.EMPTY, Insets.EMPTY)
|
||||
is TransactionCreateStatus.Failed -> BackgroundFill(Color.SALMON, CornerRadii.EMPTY, Insets.EMPTY)
|
||||
null -> BackgroundFill(Color.TRANSPARENT, CornerRadii.EMPTY, Insets.EMPTY)
|
||||
}
|
||||
label.background = Background(backgroundFill)
|
||||
label.text = if (value.first == null && value.second == null){
|
||||
"???"
|
||||
} else {
|
||||
(value.first?.toString() ?: "") + (value.second?.let { "[${it.toString()}]" } ?: "")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
transactionViewStatusUpdated.setCellValueFactory { it.value.statusUpdated }
|
||||
transactionViewCommandTypes.setCellValueFactory {
|
||||
EasyBind.map(it.value.commandTypes) { it.map { it.simpleName }.joinToString(",") }
|
||||
}
|
||||
transactionViewTotalValueEquiv.setCellValueFactory<ViewerNode, Amount<Currency>> { it.value.viewTotalValueEquiv }
|
||||
transactionViewTotalValueEquiv.cellFactory = AmountFormatter.comma.toTableCellFactory()
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
.notification-bar {
|
||||
-fx-background-color: linear-gradient(to bottom, black, darkslategray);
|
||||
}
|
||||
|
||||
.notification-bar > .notification-bar-item {
|
||||
-fx-padding: 10;
|
||||
}
|
||||
|
||||
.notification-bar > .notification-bar-item > Label {
|
||||
-fx-text-fill: white;
|
||||
-fx-font-weight: bold;
|
||||
-fx-font-size: 13;
|
||||
}
|
||||
|
||||
.notification-bar > .notification-bar-item > .progress-bar > .bar {
|
||||
-fx-padding: 8;
|
||||
}
|
||||
|
||||
.notification-bar > .notification-bar-item > .progress-bar > .track {
|
||||
-fx-opacity: 0.0;
|
||||
}
|
||||
|
||||
.notification-bar > .notification-bar-item > .button {
|
||||
-fx-base: orange;
|
||||
-fx-font-weight: bold;
|
||||
-fx-font-size: 12;
|
||||
-fx-text-fill: white;
|
||||
-fx-background-insets: 1;
|
||||
-fx-background-radius: 5;
|
||||
}
|
||||
|
||||
.thin-progress-bar > .bar {
|
||||
-fx-padding: 8;
|
||||
}
|
||||
|
||||
.thin-progress-bar > .track {
|
||||
-fx-background-color: #bce7f5;
|
||||
-fx-background-insets: 3 3 4 3;
|
||||
/*-fx-background-radius: 0.583em; *//* 7 */
|
||||
-fx-background-radius: 2;
|
||||
-fx-padding: 8;
|
||||
}
|
143
explorer/src/main/resources/com/r3corda/explorer/css/shell.css
Normal file
@ -0,0 +1,143 @@
|
||||
.root {
|
||||
-fx-font-family: "Roboto Light", sans-serif;
|
||||
-fx-font-size: 12pt;
|
||||
/* Setting the background colour explicitly is required for a correct fade/blur animation. */
|
||||
-fx-background-color: white;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
-fx-background-color: #494949;
|
||||
}
|
||||
|
||||
.sidebar-right-shadow {
|
||||
-fx-background-color: linear-gradient(to left, #1c1c1c, #494949);
|
||||
-fx-border-color: black;
|
||||
-fx-border-width: 0 0.1em 0 0;
|
||||
-fx-padding: 0 0.2em 0 0.2em;
|
||||
}
|
||||
|
||||
.sidebar-button {
|
||||
-fx-base: transparent;
|
||||
-fx-background-color: transparent;
|
||||
-fx-text-fill: white;
|
||||
-fx-graphic-text-gap: 1em;
|
||||
-fx-cursor: hand;
|
||||
-fx-effect: dropshadow(three-pass-box, black, 6, 0.0, 0, 0);
|
||||
}
|
||||
|
||||
.sidebar-button:selected {
|
||||
-fx-background-color: grey;
|
||||
}
|
||||
|
||||
.sidebar-icon {
|
||||
-fx-font-size: 25pt;
|
||||
}
|
||||
|
||||
.modal-window {
|
||||
-fx-background-color: white;
|
||||
-fx-background-radius: 5;
|
||||
-fx-effect: dropshadow(three-pass-box, black, 10, 0.0, 0, 0);
|
||||
}
|
||||
|
||||
.modal-window > .title-bar {
|
||||
-fx-background-radius: 5 5 0 0;
|
||||
-fx-background-color: #3c777b;
|
||||
-fx-alignment: center-left;
|
||||
}
|
||||
|
||||
.modal-window > .title-bar > Button {
|
||||
-fx-base: transparent;
|
||||
-fx-background-color: transparent;
|
||||
-fx-cursor: hand;
|
||||
}
|
||||
|
||||
.modal-window > .title-bar > Button:hover {
|
||||
-fx-base: #3c777b;
|
||||
-fx-background-color: -fx-shadow-highlight-color, -fx-outer-border, -fx-inner-border, -fx-body-color;
|
||||
-fx-text-fill: white;
|
||||
}
|
||||
|
||||
.modal-window > .title-bar > Label {
|
||||
-fx-text-fill: white;
|
||||
-fx-font-size: 120%;
|
||||
}
|
||||
|
||||
.large-font {
|
||||
-fx-font-size: 200%;
|
||||
}
|
||||
|
||||
/********************************************************************************************************************
|
||||
*
|
||||
* Buttons
|
||||
*
|
||||
*/
|
||||
|
||||
.flat-button {
|
||||
-fx-background-color: white;
|
||||
-fx-padding: 0 0 0 0;
|
||||
-fx-font-size: 10pt;
|
||||
}
|
||||
|
||||
.flat-button:hover {
|
||||
-fx-underline: true;
|
||||
-fx-cursor: hand;
|
||||
}
|
||||
|
||||
.flat-button:focused {
|
||||
-fx-font-weight: bold;
|
||||
}
|
||||
|
||||
.fat-buttons {
|
||||
-fx-spacing: 15.0;
|
||||
}
|
||||
|
||||
.fat-buttons Button {
|
||||
-fx-padding: 10 15 10 15;
|
||||
-fx-min-width: 100;
|
||||
-fx-font-weight: bold;
|
||||
-fx-base: whitesmoke;
|
||||
}
|
||||
|
||||
.fat-buttons Button:default {
|
||||
-fx-base: orange;
|
||||
-fx-text-fill: white;
|
||||
}
|
||||
|
||||
.fat-buttons Button:cancel {
|
||||
-fx-background-color: white;
|
||||
-fx-background-insets: 1;
|
||||
-fx-border-color: lightgray;
|
||||
-fx-border-radius: 3;
|
||||
-fx-text-fill: black;
|
||||
}
|
||||
|
||||
.fat-buttons Button:cancel:hover {
|
||||
-fx-base: white;
|
||||
-fx-background-color: -fx-shadow-highlight-color, -fx-outer-border, -fx-inner-border, -fx-body-color;
|
||||
-fx-text-fill: black;
|
||||
}
|
||||
|
||||
/** take out the focus ring */
|
||||
.no-focus-button:focused {
|
||||
-fx-background-color: -fx-shadow-highlight-color, -fx-outer-border, -fx-inner-border, -fx-body-color;
|
||||
-fx-background-insets: 0 0 -1 0, 0, 1, 2;
|
||||
-fx-background-radius: 3px, 3px, 2px, 1px;
|
||||
}
|
||||
|
||||
.blue-button {
|
||||
-fx-base: lightblue;
|
||||
-fx-text-fill: darkslategrey;
|
||||
}
|
||||
|
||||
.blue-button:disabled {
|
||||
-fx-text-fill: white;
|
||||
}
|
||||
|
||||
.green-button {
|
||||
-fx-base: #62c462;
|
||||
-fx-text-fill: darkslategrey;
|
||||
}
|
||||
|
||||
.green-button:disabled {
|
||||
-fx-text-fill: white;
|
||||
}
|
400
explorer/src/main/resources/com/r3corda/explorer/css/wallet.css
Normal file
@ -0,0 +1,400 @@
|
||||
#TopLevel.root {
|
||||
-fx-background-image:url('../images/r3bg.png');
|
||||
-fx-background-size: cover;
|
||||
-fx-background-repeat:no-repeat;
|
||||
-fx-base:white;
|
||||
}
|
||||
|
||||
#CashViewer {
|
||||
-fx-background-color: transparent;
|
||||
}
|
||||
|
||||
#CashViewerTable {
|
||||
-fx-background-color: transparent;
|
||||
}
|
||||
|
||||
#CashViewerTable .tree-table-row-cell {
|
||||
-fx-background-color: transparent;
|
||||
}
|
||||
|
||||
.root {
|
||||
-fx-padding:5px;
|
||||
}
|
||||
|
||||
.root {
|
||||
-fx-padding:5px;
|
||||
}
|
||||
|
||||
.dialog-pane {
|
||||
-fx-background-color:rgba(255,255,255,0.7);
|
||||
-fx-background-radius:2px;
|
||||
-fx-border-radius: 2px;
|
||||
-fx-border-color: rgb(20,136,204);
|
||||
}
|
||||
|
||||
|
||||
.nested-column-header, .nested-column-header {
|
||||
-fx-background-color:transparent;
|
||||
-fx-wrap-text:true;
|
||||
-fx-border-color:transparent;
|
||||
}
|
||||
|
||||
|
||||
.text-field,
|
||||
.table-column,
|
||||
.label,
|
||||
.title,
|
||||
.combo-box,
|
||||
.button,
|
||||
.split-menu-button,
|
||||
.choice-box {
|
||||
-fx-font-family:Effra;
|
||||
-fx-font-size:1em;
|
||||
-fx-text-fill:rgb(63,63,63);
|
||||
-fx-font-smoothing-type: gray;
|
||||
}
|
||||
|
||||
.text-highlight {
|
||||
-fx-text-fill:rgb(20,136,204);
|
||||
}
|
||||
|
||||
.context-menu {
|
||||
-fx-background-color:rgba(255,255,255,0.9);
|
||||
}
|
||||
|
||||
.titled-pane .content,
|
||||
.split-menu-button .label,
|
||||
.split-menu-button .arrow-button,
|
||||
.titled-pane .split-pane, .scroll-pane {
|
||||
-fx-background-color:transparent;
|
||||
}
|
||||
|
||||
|
||||
.text-field,
|
||||
.tree-table-view,
|
||||
.table-view,
|
||||
.accordion,
|
||||
.combo-box,
|
||||
.context-menu,
|
||||
.button,
|
||||
.split-menu-button,
|
||||
.choice-box,
|
||||
.titled-pane .title {
|
||||
-fx-border-color:rgb(150,150,150);
|
||||
-fx-border-width:1px;
|
||||
|
||||
}
|
||||
|
||||
.text-field:focused,
|
||||
.tree-table-view:focused,
|
||||
.table-view:focused,
|
||||
.accordion:focused,
|
||||
.combo-box:focused,
|
||||
.context-menu:focused,
|
||||
.button:focused,
|
||||
.split-menu-button:focused,
|
||||
.text-field:hover,
|
||||
.button:hover,
|
||||
.split-menu-button:hover,
|
||||
.choice-box:hover,
|
||||
.titled-pane:hover .title {
|
||||
-fx-border-color:rgb(20,136,204);
|
||||
}
|
||||
|
||||
.split-menu-button:pressed,
|
||||
.button:pressed,
|
||||
.choice-box:pressed,
|
||||
.titled-pane:expanded .title {
|
||||
-fx-background-color:rgb(20,136,204);
|
||||
-fx-text-fill:white;
|
||||
}
|
||||
|
||||
.titled-pane:expanded .title .text {
|
||||
-fx-fill:white;
|
||||
}
|
||||
.titled-pane .title,.titled-pane .title:hover {
|
||||
-fx-border-width:0.5px;
|
||||
}
|
||||
|
||||
.text-field, .combo-box, .choice-box, .password-field {
|
||||
-fx-background-color:rgba(255,255,255,0.5);
|
||||
-fx-background-radius:2px;
|
||||
-fx-border-radius: 2px;
|
||||
|
||||
|
||||
}
|
||||
|
||||
/* switch off highlighting for text-field when it's inside a combo-box */
|
||||
.combo-box .text-field, .combo-box .text-field:hover, .combo-box .text-field:focused {
|
||||
-fx-border-color:transparent;
|
||||
}
|
||||
/* table formatting */
|
||||
|
||||
.column-header-background,
|
||||
.table-column,
|
||||
.tree-table-row-cell, .column-header-background .filler {
|
||||
-fx-background-color:transparent;
|
||||
-fx-label-padding:3px;
|
||||
|
||||
|
||||
}
|
||||
|
||||
.nested-column-header .label { -fx-wrap-text:true}
|
||||
|
||||
.table-column { -fx-border-style:solid;
|
||||
-fx-border-color:rgb(216,216,216); /*t r b l */
|
||||
|
||||
-fx-border-width:0.5px;
|
||||
-fx-border-insets: 1.5px;
|
||||
-fx-background-insets: 2px;
|
||||
}
|
||||
|
||||
|
||||
.tree-table-row-cell:even .table-column,
|
||||
.table-row-cell:even .table-column,
|
||||
.title, .split-menu-button, .button {
|
||||
-fx-background-color: rgba(20,136,204,0.2);
|
||||
}
|
||||
|
||||
.table-row-cell:selected, .tree-table-row-cell:selected {
|
||||
-fx-background-color:transparent;
|
||||
}
|
||||
.tree-table-row-cell:selected .table-column,
|
||||
.tree-table-row-cell:focused .table-column,
|
||||
.table-row-cell:selected .table-column,
|
||||
.table-row-cell:focused .table-column {
|
||||
-fx-background-color:rgba(20,136,204,0.8);
|
||||
|
||||
|
||||
}
|
||||
.bad .text {
|
||||
-fx-fill:rgb(236,29,36);
|
||||
}
|
||||
|
||||
.table-row-cell:focused .table-column .text,
|
||||
.tree-table-row-cell:focused .table-column .text {
|
||||
-fx-fill:white;
|
||||
}
|
||||
|
||||
.table-column:hover,
|
||||
.table-row-cell:hover .first-column,
|
||||
.table-row-cell:hover .second-column,
|
||||
.tree-table-row-cell:hover .first-column,
|
||||
.tree-table-row-cell:hover .second-column {
|
||||
-fx-border-color:rgb(20,136,204);
|
||||
}
|
||||
|
||||
.tree-table-view .column-header-background .nested-column-header .table-column,
|
||||
.table-view .column-header-background .nested-column-header .table-column {
|
||||
-fx-border-color:transparent;
|
||||
}
|
||||
|
||||
/* Special formatting - columns to be presented with no join between them */
|
||||
|
||||
.first-column {
|
||||
-fx-border-width:0.5px 0px 0.5px 0.5px;
|
||||
-fx-border-insets: 1.5px 0px 1.5px 1.5px;
|
||||
-fx-background-insets: 2px 0px 2px 2px;
|
||||
}
|
||||
|
||||
|
||||
.second-column {
|
||||
-fx-border-width:0.5px 0.5px 0.5px 0px;
|
||||
-fx-border-insets: 1.5px 1.5px 1.5px 0px;
|
||||
-fx-background-insets: 2px 2px 2px 0px;
|
||||
}
|
||||
|
||||
|
||||
/* highlighting where the user has typed a key */
|
||||
.tree-table-view text-area, .table-view text-area{
|
||||
-fx-font-weight:bold;
|
||||
-fx-fill:rgb(20,136,204);
|
||||
|
||||
}
|
||||
|
||||
.tree-table-row-cell:selected .table-column text-area,
|
||||
.table-row-cell:selected .table-column text-area
|
||||
{
|
||||
-fx-font-weight:bold;
|
||||
-fx-fill:rgb(255,255,255);
|
||||
}
|
||||
|
||||
|
||||
/* labels */
|
||||
.dialog-pane .header-panel .label .text{
|
||||
-fx-font-size:1em;
|
||||
-fx-fill:rgb(20,136,204);
|
||||
}
|
||||
|
||||
#headline, .headline {
|
||||
-fx-font-size:2.4em;
|
||||
}
|
||||
#subline, .subline {
|
||||
-fx-font-size:1.4em;
|
||||
}
|
||||
#headline, #subline {
|
||||
-fx-text-fill:rgb(65,65,65);
|
||||
-fx-padding:0px;
|
||||
|
||||
}
|
||||
|
||||
/* search boxes */
|
||||
.search {
|
||||
-fx-background-image:url('../images/search.png');
|
||||
-fx-background-size:Auto 16px;
|
||||
-fx-background-repeat:no-repeat;
|
||||
-fx-background-position:8px center;
|
||||
-fx-padding:5px 5px 5px 30px;
|
||||
-fx-background-radius: 2px;
|
||||
-fx-border-radius: 2px;
|
||||
}
|
||||
|
||||
.search-clear {
|
||||
-fx-image:url('../images/clear_inactive.png');
|
||||
}
|
||||
|
||||
.search-clear:hover {
|
||||
-fx-image:url('../images/clear.png');
|
||||
}
|
||||
|
||||
.split-menu-button, .button, .choice-box {
|
||||
-fx-background-radius:2px;
|
||||
-fx-border-radius: 2px;
|
||||
-fx-border-insets: 0.5px;
|
||||
-fx-background-insets:0.5px;
|
||||
|
||||
}
|
||||
|
||||
.tree-table-row-cell .monetary-value, .monetary-value .label, .table-row-cell .monetary-value {
|
||||
-fx-alignment:center-right;
|
||||
}
|
||||
|
||||
|
||||
/* split panes */
|
||||
.split-pane-divider {
|
||||
-fx-background-color: transparent;
|
||||
-fx-border-color: rgb(160,160,160);
|
||||
-fx-border-width: 0 0 0 0.5px
|
||||
}
|
||||
|
||||
/* Dashboard tiles */
|
||||
|
||||
.tile,.tile-user {
|
||||
-fx-padding: 10px;
|
||||
-fx-pref-height:200px; -fx-pref-width:200px;
|
||||
|
||||
|
||||
}
|
||||
.tile .title, .tile:expanded .title,
|
||||
.tile-user .title, .tile-user:expanded .title {
|
||||
-fx-alignment:center-right;
|
||||
-fx-font-size:1.4em;
|
||||
-fx-font-weight:bold;
|
||||
-fx-cursor:hand;
|
||||
-fx-background-radius:2px 2px 0 0;
|
||||
-fx-border-radius: 2px 2px 0 0;
|
||||
-fx-border-width:1px 1px 0 1px;
|
||||
-fx-background-color: rgba(255,255,255,0.5);
|
||||
-fx-border-color:rgb(160,160,160); /*t r b l */
|
||||
|
||||
}
|
||||
|
||||
.tile .title .text, .tile:expanded .title .text,
|
||||
.tile-user .title .text, .tile-user:expanded .title .text {
|
||||
-fx-fill:rgb(65,65,65);
|
||||
}
|
||||
.tile .content,
|
||||
.tile-user .content {
|
||||
-fx-background-color: rgba(255,255,255,0.7);
|
||||
-fx-background-size:Auto 90%;
|
||||
-fx-background-repeat:no-repeat;
|
||||
-fx-background-position:center center;
|
||||
-fx-cursor:hand;
|
||||
-fx-background-radius:0 0 2px 2px;
|
||||
-fx-border-radius: 0 0 2px 2px;
|
||||
-fx-padding:0px;
|
||||
-fx-alignment:bottom-right;
|
||||
-fx-border-color:rgb(150,150,150); /*t r b l */
|
||||
}
|
||||
.tile .label,
|
||||
.tile-user .label {
|
||||
-fx-font-size:2.4em;
|
||||
-fx-padding:20px;
|
||||
-fx-text-fill:rgb(65,65,65);
|
||||
-fx-font-weight:normal;
|
||||
-fx-text-alignment:right;
|
||||
|
||||
}
|
||||
|
||||
.tile:hover .label,
|
||||
.tile-user:hover .label {
|
||||
-fx-padding:24px;
|
||||
}
|
||||
|
||||
.tile:hover .content, .tile:hover .title,
|
||||
.tile-user:hover .content, .tile-user:hover .title {
|
||||
-fx-border-color:rgb(20,136,204);
|
||||
-fx-background-color: rgb(20,136,204);
|
||||
|
||||
}
|
||||
.tile:hover, .tile-user:hover {
|
||||
-fx-padding:4px;
|
||||
}
|
||||
|
||||
.tile:hover .label, .tile:hover .label .text, .tile:hover .title .text {
|
||||
-fx-text-fill:rgb(255,255,255);
|
||||
-fx-fill:rgb(255,255,255);
|
||||
-fx-font-weight:bold;
|
||||
-fx-effect:none;
|
||||
}
|
||||
|
||||
#tile_cash .content {
|
||||
-fx-background-image:url('../images/cash_lrg.png');
|
||||
}
|
||||
#tile_debtors .content {
|
||||
-fx-background-image:url('../images/outflow_lrg.png');
|
||||
}
|
||||
#tile_creditors .content {
|
||||
-fx-background-image:url('../images/inflow_lrg.png');
|
||||
}
|
||||
#tile_tx .content {
|
||||
-fx-background-image:url('../images/tx_lrg.png');
|
||||
}
|
||||
|
||||
#tile_cpty .content {
|
||||
-fx-background-image:url('../images/cpty_lrg.png');
|
||||
}
|
||||
|
||||
.tile-user .content {
|
||||
-fx-background-image:url('../images/user_b.png');
|
||||
}
|
||||
.tile-user-test-man .content {
|
||||
-fx-background-image:url('../images/man1.png');
|
||||
-fx-background-size:cover;
|
||||
}
|
||||
.tile-user-test-woman .content {
|
||||
-fx-background-image:url('../images/woman1.png');
|
||||
-fx-background-size:cover;
|
||||
}
|
||||
|
||||
.tile-user .label {
|
||||
-fx-background-color:rgba(255,255,255,0.7);
|
||||
}
|
||||
|
||||
.tile-user:hover .title, .tile-user:hover .content {
|
||||
-fx-background-color:rgba(255,255,255,0.7);
|
||||
}
|
||||
|
||||
.counterparty {
|
||||
-fx-background-image:url('../images/inst_128.png');
|
||||
-fx-background-size:Auto 16px;
|
||||
-fx-background-repeat: no-repeat;
|
||||
-fx-background-position:0px center;
|
||||
-fx-padding:0 0 0 20px;
|
||||
}
|
||||
|
||||
.state-panel{
|
||||
-fx-background-color: rgba(255,255,255,0.7);
|
||||
-fx-border-color:rgb(150,150,150);
|
||||
-fx-insets:5px
|
||||
}
|
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.5 KiB |
BIN
explorer/src/main/resources/com/r3corda/explorer/images/cash.png
Normal file
After Width: | Height: | Size: 3.1 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 3.2 KiB |
After Width: | Height: | Size: 18 KiB |
BIN
explorer/src/main/resources/com/r3corda/explorer/images/home.png
Normal file
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 7.0 KiB |
BIN
explorer/src/main/resources/com/r3corda/explorer/images/inst.png
Normal file
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 4.4 KiB |
BIN
explorer/src/main/resources/com/r3corda/explorer/images/man1.png
Normal file
After Width: | Height: | Size: 326 KiB |
After Width: | Height: | Size: 6.8 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 3.7 KiB |
BIN
explorer/src/main/resources/com/r3corda/explorer/images/tx.png
Normal file
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 12 KiB |
@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
<svg id="svg15261" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="297mm" width="210mm" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" viewBox="0 0 744.09448819 1052.3622047">
|
||||
<metadata id="metadata15266">
|
||||
<rdf:RDF>
|
||||
<cc:Work rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
|
||||
<dc:title/>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g id="layer1">
|
||||
<g id="g16332" transform="matrix(22.359 0 0 22.359 -19527 -23416)" fill="#1488cc">
|
||||
<g id="g45283" fill="#1488cc">
|
||||
<path id="path6608" d="m891.63 1061.7c0 2.0866-1.695 3.7802-3.7844 3.7802-2.0872 0-3.7822-1.6936-3.7822-3.7802 0-2.0906 1.695-3.7826 3.7822-3.7826 2.0894 0 3.7844 1.692 3.7844 3.7826"/>
|
||||
<path id="path6610" d="m894.18 1076.1c0 2.4219-1.633 2.7474-3.6493 2.7474h-5.3908c-2.0119 0-3.6448-0.3255-3.6448-2.7474l1.0547-6.3223c0-2.02 1.3627-3.6582 3.0422-3.6582h4.4913c1.6795 0 3.04 1.6382 3.04 3.6582l1.0569 6.3223"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 8.8 KiB |
After Width: | Height: | Size: 485 KiB |
@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.layout.HBox?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
|
||||
<HBox spacing="5.0" xmlns="http://javafx.com/javafx/8.0.76-ea" xmlns:fx="http://javafx.com/fxml/1">
|
||||
<children>
|
||||
<VBox HBox.hgrow="SOMETIMES">
|
||||
<children>
|
||||
<Label text="State ID" VBox.vgrow="ALWAYS" />
|
||||
<Label text="Issuer" />
|
||||
<Label text="Originated" wrapText="true" />
|
||||
<Label text="Amount" wrapText="true" />
|
||||
<Label fx:id="EquivLabel" text="USD" wrapText="true" />
|
||||
</children>
|
||||
</VBox>
|
||||
<VBox HBox.hgrow="ALWAYS">
|
||||
<children>
|
||||
<Label fx:id="StateIdValueLabel" text="39043-329090-390091" />
|
||||
<Label fx:id="IssuerValueLabel" styleClass="counterparty" text="C-03820 HSBC GROUP PLC" />
|
||||
<Label fx:id="OriginatedValueLabel" text="2018-04-27 11:34 UTC" />
|
||||
<Label fx:id="AmountValueLabel" text="GBP 0.00" wrapText="true" />
|
||||
<Label fx:id="EquivValueLabel" text="0.00" wrapText="true" />
|
||||
</children>
|
||||
</VBox>
|
||||
</children>
|
||||
</HBox>
|
@ -0,0 +1,84 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import java.lang.String?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.control.ListView?>
|
||||
<?import javafx.scene.control.SplitPane?>
|
||||
<?import javafx.scene.control.TextField?>
|
||||
<?import javafx.scene.control.TreeTableColumn?>
|
||||
<?import javafx.scene.control.TreeTableView?>
|
||||
<?import javafx.scene.image.Image?>
|
||||
<?import javafx.scene.image.ImageView?>
|
||||
<?import javafx.scene.layout.StackPane?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
|
||||
<SplitPane fx:id="TopSplitPane" dividerPositions="0.5" xmlns="http://javafx.com/javafx/8.0.76-ea" xmlns:fx="http://javafx.com/fxml/1">
|
||||
<items>
|
||||
<VBox fx:id="LeftPane" spacing="5.0" styleClass="root">
|
||||
<children>
|
||||
<StackPane alignment="CENTER_RIGHT">
|
||||
<VBox.margin>
|
||||
<Insets />
|
||||
</VBox.margin>
|
||||
<children>
|
||||
<TextField id="search" fx:id="SearchCriteriaTextField" promptText="Search by issuer/currency" styleClass="search">
|
||||
<opaqueInsets>
|
||||
<Insets />
|
||||
</opaqueInsets>
|
||||
<StackPane.margin>
|
||||
<Insets />
|
||||
</StackPane.margin>
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</padding>
|
||||
</TextField>
|
||||
<ImageView fx:id="SearchCancelImageView" fitHeight="16.0" fitWidth="16.0" pickOnBounds="true" preserveRatio="true" styleClass="search-clear">
|
||||
<image>
|
||||
<Image url="@../../images/clear_inactive.png" />
|
||||
</image>
|
||||
<StackPane.margin>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</StackPane.margin>
|
||||
</ImageView>
|
||||
</children>
|
||||
</StackPane>
|
||||
<Label fx:id="TotalMatchingLabel" alignment="BOTTOM_LEFT" text="Total 15 matching issuer(s)" wrapText="true">
|
||||
<VBox.margin>
|
||||
<Insets bottom="5.0" right="10.0" top="5.0" />
|
||||
</VBox.margin>
|
||||
</Label>
|
||||
<TreeTableView fx:id="CashViewerTable" showRoot="false" VBox.vgrow="ALWAYS">
|
||||
<columns>
|
||||
<TreeTableColumn fx:id="CashViewerTableIssuerCurrency" maxWidth="1.7976931348623157E308" minWidth="-1.0" prefWidth="100.0" styleClass="first-column" text="Issuer/Currency" />
|
||||
<TreeTableColumn fx:id="CashViewerTableLocalCurrency" maxWidth="1.7976931348623157E308" minWidth="-1.0" prefWidth="132.0" text="Local currency">
|
||||
<styleClass>
|
||||
<String fx:value="monetary-value" />
|
||||
<String fx:value="second-column" />
|
||||
</styleClass>
|
||||
</TreeTableColumn>
|
||||
<TreeTableColumn fx:id="CashViewerTableEquiv" maxWidth="1.7976931348623157E308" minWidth="-1.0" prefWidth="72.0" styleClass="monetary-value" text="Equiv" />
|
||||
</columns>
|
||||
</TreeTableView>
|
||||
</children>
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</padding>
|
||||
</VBox>
|
||||
<VBox fx:id="RightPane" spacing="5.0">
|
||||
<children>
|
||||
<Button mnemonicParsing="false" text=">>" />
|
||||
<Label fx:id="TotalPositionsLabel" styleClass="subline" text="Total 18 position(s)" />
|
||||
<Label fx:id="EquivSumLabel" styleClass="headline" text="USD 394.6k" />
|
||||
<ListView fx:id="CashStatesList" VBox.vgrow="ALWAYS" />
|
||||
</children>
|
||||
<opaqueInsets>
|
||||
<Insets />
|
||||
</opaqueInsets>
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</padding>
|
||||
</VBox>
|
||||
</items>
|
||||
</SplitPane>
|
@ -0,0 +1,95 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.control.MenuItem?>
|
||||
<?import javafx.scene.control.SplitMenuButton?>
|
||||
<?import javafx.scene.control.TextField?>
|
||||
<?import javafx.scene.image.Image?>
|
||||
<?import javafx.scene.image.ImageView?>
|
||||
<?import javafx.scene.layout.HBox?>
|
||||
<?import javafx.scene.layout.StackPane?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
|
||||
<VBox xmlns="http://javafx.com/javafx/8.0.76-ea" xmlns:fx="http://javafx.com/fxml/1">
|
||||
<children>
|
||||
<VBox spacing="5.0">
|
||||
<children>
|
||||
<HBox>
|
||||
<children>
|
||||
<HBox alignment="CENTER_LEFT" spacing="5.0" HBox.hgrow="ALWAYS">
|
||||
<children>
|
||||
<VBox fx:id="SectionIconContainer" alignment="CENTER">
|
||||
<children>
|
||||
<ImageView fx:id="SectionIcon" fitHeight="30.0" fitWidth="30.0" pickOnBounds="true" preserveRatio="true">
|
||||
<image>
|
||||
<Image url="@../../images/home.png" />
|
||||
</image>
|
||||
</ImageView>
|
||||
</children>
|
||||
</VBox>
|
||||
<Label id="headline" fx:id="SectionLabel" text="Home" />
|
||||
</children>
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</padding>
|
||||
</HBox>
|
||||
<HBox alignment="TOP_RIGHT" spacing="5.0">
|
||||
<children>
|
||||
<Button fx:id="DebugNextButton" mnemonicParsing="false" text="Next" />
|
||||
<Button fx:id="DebugGoStopButton" mnemonicParsing="false" text="!!!" />
|
||||
<SplitMenuButton mnemonicParsing="false" text="DRUTTER">
|
||||
<items>
|
||||
<MenuItem mnemonicParsing="false" text="Sign out" />
|
||||
<MenuItem mnemonicParsing="false" text="Account settings..." />
|
||||
</items>
|
||||
<graphic>
|
||||
<ImageView fitHeight="20.0" fitWidth="52.0" pickOnBounds="true" preserveRatio="true">
|
||||
<image>
|
||||
<Image url="@../../images/user_w.png" />
|
||||
</image>
|
||||
</ImageView>
|
||||
</graphic>
|
||||
</SplitMenuButton>
|
||||
<Button fx:id="SettingsButton" mnemonicParsing="false" text="Settings">
|
||||
<graphic>
|
||||
<ImageView fitHeight="20.0" fitWidth="20.0" pickOnBounds="true" preserveRatio="true">
|
||||
<image>
|
||||
<Image url="@../../images/settings_w.png" />
|
||||
</image>
|
||||
</ImageView>
|
||||
</graphic>
|
||||
</Button>
|
||||
</children>
|
||||
</HBox>
|
||||
</children>
|
||||
</HBox>
|
||||
<StackPane alignment="CENTER_RIGHT">
|
||||
<children>
|
||||
<TextField id="search_main" promptText="Search for states, transactions, counterparties etc." styleClass="search">
|
||||
<opaqueInsets>
|
||||
<Insets />
|
||||
</opaqueInsets>
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="30.0" right="5.0" top="5.0" />
|
||||
</padding>
|
||||
</TextField>
|
||||
<ImageView fitHeight="16.0" fitWidth="16.0" pickOnBounds="true" preserveRatio="true" styleClass="search-clear">
|
||||
<image>
|
||||
<Image url="@../../images/clear_inactive.png" />
|
||||
</image>
|
||||
<StackPane.margin>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</StackPane.margin>
|
||||
</ImageView>
|
||||
</children>
|
||||
</StackPane>
|
||||
</children>
|
||||
</VBox>
|
||||
<StackPane alignment="CENTER_RIGHT" />
|
||||
</children>
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</padding>
|
||||
</VBox>
|
@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.control.TitledPane?>
|
||||
<?import javafx.scene.layout.TilePane?>
|
||||
|
||||
<TilePane prefHeight="425.0" prefWidth="425.0" tileAlignment="TOP_LEFT" xmlns="http://javafx.com/javafx/8.0.76-ea" xmlns:fx="http://javafx.com/fxml/1">
|
||||
<children>
|
||||
<TitledPane id="tile_cash" fx:id="OurCashPane" alignment="CENTER" collapsible="false" prefHeight="160.0" prefWidth="160.0" styleClass="tile" text="Our cash">
|
||||
<content>
|
||||
<Label fx:id="OurCashLabel" text="USD 186.7m" textAlignment="CENTER" wrapText="true" />
|
||||
</content>
|
||||
</TitledPane>
|
||||
<TitledPane id="tile_debtors" fx:id="OurDebtorsPane" alignment="CENTER" collapsible="false" layoutX="232.0" layoutY="10.0" prefHeight="160.0" prefWidth="160.0" styleClass="tile" text="Our debtors">
|
||||
<content>
|
||||
<Label text="USD 71.3m" textAlignment="CENTER" wrapText="true" />
|
||||
</content>
|
||||
</TitledPane>
|
||||
<TitledPane id="tile_creditors" fx:id="OurCreditorsPane" alignment="CENTER" collapsible="false" layoutX="312.0" layoutY="10.0" prefHeight="160.0" prefWidth="160.0" styleClass="tile" text="Our creditors">
|
||||
<content>
|
||||
<Label text="USD (29.4m)" textAlignment="CENTER" wrapText="true" />
|
||||
</content>
|
||||
</TitledPane>
|
||||
<TitledPane id="tile_tx" fx:id="OurTransactionsPane" alignment="CENTER" collapsible="false" layoutX="392.0" layoutY="10.0" prefHeight="160.0" prefWidth="160.0" styleClass="tile" text="Our transactions">
|
||||
<content>
|
||||
<Label fx:id="OurTransactionsLabel" text="In flight: 1,315" textAlignment="CENTER" wrapText="true" />
|
||||
</content>
|
||||
</TitledPane>
|
||||
</children>
|
||||
</TilePane>
|
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.scene.layout.BorderPane?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
|
||||
<VBox fx:id="TopLevel" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" xmlns="http://javafx.com/javafx/8.0.76-ea" xmlns:fx="http://javafx.com/fxml/1">
|
||||
<children>
|
||||
<BorderPane fx:id="SelectionBorderPane" />
|
||||
</children>
|
||||
</VBox>
|
@ -0,0 +1,165 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import java.lang.String?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.Accordion?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.control.SplitPane?>
|
||||
<?import javafx.scene.control.TableColumn?>
|
||||
<?import javafx.scene.control.TableView?>
|
||||
<?import javafx.scene.control.TextField?>
|
||||
<?import javafx.scene.control.TitledPane?>
|
||||
<?import javafx.scene.image.Image?>
|
||||
<?import javafx.scene.image.ImageView?>
|
||||
<?import javafx.scene.layout.AnchorPane?>
|
||||
<?import javafx.scene.layout.StackPane?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
|
||||
<VBox styleClass="view" xmlns="http://javafx.com/javafx/8.0.76-ea" xmlns:fx="http://javafx.com/fxml/1">
|
||||
<children>
|
||||
<StackPane alignment="CENTER_RIGHT">
|
||||
<children>
|
||||
<TextField promptText="Filter transactions by originator, contract type..." styleClass="search">
|
||||
<opaqueInsets>
|
||||
<Insets />
|
||||
</opaqueInsets>
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="30.0" right="5.0" top="5.0" />
|
||||
</padding>
|
||||
<StackPane.margin>
|
||||
<Insets bottom="5.0" top="5.0" />
|
||||
</StackPane.margin>
|
||||
</TextField>
|
||||
<ImageView fitHeight="16.0" fitWidth="16.0" pickOnBounds="true" preserveRatio="true" styleClass="search-clear">
|
||||
<image>
|
||||
<Image url="@../../images/clear_inactive.png" />
|
||||
</image>
|
||||
<StackPane.margin>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</StackPane.margin>
|
||||
</ImageView>
|
||||
</children>
|
||||
</StackPane>
|
||||
<SplitPane dividerPositions="0.5" orientation="VERTICAL" prefHeight="562.0" prefWidth="1087.0" VBox.vgrow="ALWAYS">
|
||||
<items>
|
||||
<TableView fx:id="TransactionViewTable" prefHeight="200.0" prefWidth="200.0">
|
||||
<columns>
|
||||
<TableColumn fx:id="TransactionViewTransactionId" prefWidth="187.0" text="Transaction ID" />
|
||||
<TableColumn fx:id="TransactionViewOriginator" prefWidth="174.0" text="Originator" />
|
||||
<TableColumn fx:id="TransactionViewTransactionStatus" prefWidth="75.0" text="Transaction status" />
|
||||
<TableColumn fx:id="TransactionViewStatusUpdated" prefWidth="75.0" text="Status updated" />
|
||||
<TableColumn fx:id="TransactionViewCommandTypes" prefWidth="75.0" text="Command type(s)" />
|
||||
<TableColumn fx:id="TransactionViewTotalValueEquiv" prefWidth="75.0" styleClass="monetary-value" text="Total value (USD equiv)" />
|
||||
</columns>
|
||||
<columnResizePolicy>
|
||||
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY" />
|
||||
</columnResizePolicy>
|
||||
</TableView>
|
||||
<Accordion>
|
||||
<panes>
|
||||
<TitledPane text="Transaction details">
|
||||
<content>
|
||||
<AnchorPane>
|
||||
<children>
|
||||
<Label layoutX="5.0" layoutY="5.0" text="Transaction ID" />
|
||||
<Label layoutX="97.0" layoutY="5.0" text="IC-3902-29090-32091" AnchorPane.leftAnchor="95.0" AnchorPane.rightAnchor="15.0" />
|
||||
<Label layoutX="5.0" layoutY="33.0" text="Originator" />
|
||||
<Label layoutX="97.0" layoutY="33.0" prefHeight="16.0" text="C03102 HSBC GROUP PLC" AnchorPane.leftAnchor="95.0" AnchorPane.rightAnchor="15.0" />
|
||||
<Label layoutX="5.0" layoutY="49.0" prefHeight="26.0" prefWidth="78.0" text="Date/time originated" wrapText="true" />
|
||||
<Label layoutX="97.0" layoutY="49.0" text="2019-05-24 11:47 UTC" AnchorPane.leftAnchor="95.0" AnchorPane.rightAnchor="15.0" />
|
||||
<Label layoutX="5.0" layoutY="90.0" text="Current status" />
|
||||
<Label layoutX="104.0" layoutY="90.0" prefHeight="16.0" text="01800 - EXCEPTION - UNSPECIFIED NETWORK ERROR" AnchorPane.leftAnchor="95.0" AnchorPane.rightAnchor="15.0" />
|
||||
</children>
|
||||
</AnchorPane>
|
||||
</content></TitledPane>
|
||||
<TitledPane text="Contract states">
|
||||
<content>
|
||||
<SplitPane dividerPositions="0.5" prefHeight="160.0" prefWidth="200.0">
|
||||
<items>
|
||||
<VBox prefHeight="200.0" prefWidth="100.0">
|
||||
<children>
|
||||
<Label text="Inputs: 23">
|
||||
<padding>
|
||||
<Insets bottom="5.0" top="5.0" />
|
||||
</padding></Label>
|
||||
<TableView prefHeight="200.0" prefWidth="200.0" VBox.vgrow="ALWAYS">
|
||||
<columns>
|
||||
<TableColumn prefWidth="75.0" text="ID" />
|
||||
<TableColumn prefWidth="75.0" text="Type" />
|
||||
<TableColumn prefWidth="75.0" text="Owner" />
|
||||
<TableColumn prefWidth="75.0" styleClass="first-column" text="Local Ccy" />
|
||||
<TableColumn prefWidth="75.0" text="Amount">
|
||||
<styleClass>
|
||||
<String fx:value="second-column" />
|
||||
<String fx:value="monetary-value" />
|
||||
</styleClass>
|
||||
</TableColumn>
|
||||
<TableColumn prefWidth="75.0" text="USD Equiv" />
|
||||
</columns>
|
||||
<columnResizePolicy>
|
||||
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY" />
|
||||
</columnResizePolicy>
|
||||
</TableView>
|
||||
</children>
|
||||
</VBox>
|
||||
<VBox prefHeight="200.0" prefWidth="100.0">
|
||||
<children>
|
||||
<Label text="Outputs: 24">
|
||||
<VBox.margin>
|
||||
<Insets top="5.0" />
|
||||
</VBox.margin>
|
||||
<padding>
|
||||
<Insets bottom="5.0" />
|
||||
</padding></Label>
|
||||
<TableView prefHeight="200.0" prefWidth="200.0" VBox.vgrow="ALWAYS">
|
||||
<columns>
|
||||
<TableColumn prefWidth="75.0" text="ID" />
|
||||
<TableColumn prefWidth="75.0" text="Type" />
|
||||
<TableColumn prefWidth="75.0" text="Owner" />
|
||||
<TableColumn prefWidth="75.0" styleClass="first-column" text="Local Ccy" />
|
||||
<TableColumn prefWidth="75.0" text="Amount">
|
||||
<styleClass>
|
||||
<String fx:value="second-column" />
|
||||
<String fx:value="monetary-value" />
|
||||
</styleClass>
|
||||
</TableColumn>
|
||||
<TableColumn prefWidth="75.0" text="USD Equiv" />
|
||||
</columns>
|
||||
<columnResizePolicy>
|
||||
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY" />
|
||||
</columnResizePolicy>
|
||||
</TableView>
|
||||
</children>
|
||||
</VBox>
|
||||
</items>
|
||||
</SplitPane>
|
||||
</content>
|
||||
</TitledPane>
|
||||
<TitledPane prefHeight="200.0" prefWidth="200.0" text="Signatures (3/4)">
|
||||
<content>
|
||||
<TableView prefHeight="200.0" prefWidth="200.0">
|
||||
<columns>
|
||||
<TableColumn prefWidth="305.0" text="Signatory" />
|
||||
<TableColumn prefWidth="221.0" text="Status" />
|
||||
<TableColumn prefWidth="271.0" text="Status updated" />
|
||||
</columns>
|
||||
</TableView>
|
||||
</content>
|
||||
</TitledPane>
|
||||
<TitledPane prefHeight="54.0" prefWidth="722.0" text="Attachments (3)">
|
||||
<content>
|
||||
<TableView prefHeight="200.0" prefWidth="200.0">
|
||||
<columns>
|
||||
<TableColumn prefWidth="136.0" text="File name" />
|
||||
<TableColumn minWidth="8.0" prefWidth="172.0" text="Size" />
|
||||
<TableColumn prefWidth="218.0" text="Date modified" />
|
||||
</columns>
|
||||
</TableView>
|
||||
</content></TitledPane>
|
||||
</panes>
|
||||
</Accordion>
|
||||
</items>
|
||||
</SplitPane>
|
||||
<Label text="133 matching transaction(s)" />
|
||||
</children>
|
||||
</VBox>
|
@ -0,0 +1,65 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import java.lang.String?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.ComboBox?>
|
||||
<?import javafx.scene.control.DialogPane?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.control.PasswordField?>
|
||||
<?import javafx.scene.control.TextField?>
|
||||
<?import javafx.scene.control.TitledPane?>
|
||||
<?import javafx.scene.layout.TilePane?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
|
||||
<DialogPane expanded="true" headerText="Sign in to Corda" prefHeight="246.0" prefWidth="648.0" xmlns="http://javafx.com/javafx/8.0.60" xmlns:fx="http://javafx.com/fxml/1">
|
||||
<content>
|
||||
<TilePane alignment="CENTER">
|
||||
<children>
|
||||
<TitledPane alignment="CENTER" collapsible="false" prefHeight="160.0" prefWidth="160.0" text="Log in">
|
||||
<content>
|
||||
<Label alignment="BOTTOM_RIGHT" prefHeight="200.0" prefWidth="200.0" text="Thomas" textAlignment="CENTER" wrapText="true" />
|
||||
</content>
|
||||
<styleClass>
|
||||
<String fx:value="tile-user" />
|
||||
<String fx:value="tile-user-test-man" />
|
||||
</styleClass>
|
||||
</TitledPane>
|
||||
<TitledPane alignment="CENTER" collapsible="false" layoutX="232.0" layoutY="10.0" prefHeight="160.0" prefWidth="160.0" text="Log in">
|
||||
<styleClass>
|
||||
<String fx:value="tile-user" />
|
||||
<String fx:value="tile-user-test-woman" />
|
||||
</styleClass>
|
||||
<content>
|
||||
<Label alignment="BOTTOM_RIGHT" prefHeight="200.0" prefWidth="200.0" text="Theresa" textAlignment="CENTER" wrapText="true" />
|
||||
</content>
|
||||
</TitledPane>
|
||||
<TitledPane alignment="CENTER" collapsible="false" layoutX="312.0" layoutY="10.0" prefHeight="160.0" prefWidth="160.0" styleClass="tile-user" text="Log in">
|
||||
<content>
|
||||
<Label alignment="BOTTOM_RIGHT" prefHeight="200.0" prefWidth="200.0" text="Other user" textAlignment="CENTER" wrapText="true" />
|
||||
</content>
|
||||
</TitledPane>
|
||||
</children>
|
||||
</TilePane>
|
||||
</content>
|
||||
<expandableContent>
|
||||
<VBox alignment="TOP_CENTER">
|
||||
<children>
|
||||
<ComboBox editable="true" maxWidth="300.0" prefWidth="300.0" promptText="Server name">
|
||||
<VBox.margin>
|
||||
<Insets top="5.0" />
|
||||
</VBox.margin>
|
||||
</ComboBox>
|
||||
<TextField maxWidth="300.0" promptText="User name">
|
||||
<VBox.margin>
|
||||
<Insets top="5.0" />
|
||||
</VBox.margin>
|
||||
</TextField>
|
||||
<PasswordField maxWidth="300.0" prefWidth="300.0" promptText="Password">
|
||||
<VBox.margin>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</VBox.margin>
|
||||
</PasswordField>
|
||||
</children>
|
||||
</VBox>
|
||||
</expandableContent>
|
||||
</DialogPane>
|
@ -0,0 +1,75 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import java.lang.String?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.control.TextField?>
|
||||
<?import javafx.scene.control.TreeTableColumn?>
|
||||
<?import javafx.scene.control.TreeTableView?>
|
||||
<?import javafx.scene.image.Image?>
|
||||
<?import javafx.scene.image.ImageView?>
|
||||
<?import javafx.scene.layout.StackPane?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
|
||||
<VBox xmlns="http://javafx.com/javafx/8.0.60" xmlns:fx="http://javafx.com/fxml/1">
|
||||
<children>
|
||||
<StackPane alignment="CENTER_RIGHT">
|
||||
<VBox.margin>
|
||||
<Insets bottom="5.0" />
|
||||
</VBox.margin>
|
||||
<children>
|
||||
<TextField promptText="Search by creditor" styleClass="search">
|
||||
<opaqueInsets>
|
||||
<Insets />
|
||||
</opaqueInsets>
|
||||
<StackPane.margin>
|
||||
<Insets />
|
||||
</StackPane.margin>
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="30.0" right="5.0" top="5.0" />
|
||||
</padding>
|
||||
</TextField>
|
||||
<ImageView id="clear" fitHeight="16.0" fitWidth="16.0" pickOnBounds="true" preserveRatio="true" styleClass="search-clear">
|
||||
<image>
|
||||
<Image url="@Desktop/clear_inactive.png" />
|
||||
</image>
|
||||
<StackPane.margin>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</StackPane.margin>
|
||||
</ImageView>
|
||||
</children>
|
||||
</StackPane>
|
||||
<VBox />
|
||||
<TreeTableView showRoot="false" sortMode="ONLY_FIRST_LEVEL" VBox.vgrow="ALWAYS">
|
||||
<columns>
|
||||
<TreeTableColumn prefWidth="240.0" styleClass="first-column" text="Creditor" />
|
||||
<TreeTableColumn editable="false" minWidth="0.0" prefWidth="122.0" text="Total outstanding">
|
||||
<styleClass>
|
||||
<String fx:value="montetary-value" />
|
||||
<String fx:value="second-column" />
|
||||
</styleClass></TreeTableColumn>
|
||||
<TreeTableColumn editable="false" minWidth="0.0" prefWidth="156.0" styleClass="montetary-value" text="Overdue" />
|
||||
<TreeTableColumn prefWidth="75.0" styleClass="montetary-value" text="1d" />
|
||||
<TreeTableColumn prefWidth="75.0" styleClass="montetary-value" text="2-7d" />
|
||||
<TreeTableColumn prefWidth="75.0" styleClass="montetary-value" text="8-14d" />
|
||||
<TreeTableColumn prefWidth="75.0" styleClass="montetary-value" text="14d-1m" />
|
||||
<TreeTableColumn prefWidth="75.0" styleClass="montetary-value" text="1-3m" />
|
||||
<TreeTableColumn prefWidth="75.0" styleClass="montetary-value" text="3-6m" />
|
||||
<TreeTableColumn prefWidth="75.0" styleClass="montetary-value" text="6m-1yr" />
|
||||
<TreeTableColumn prefWidth="75.0" styleClass="montetary-value" text="1-5yr" />
|
||||
<TreeTableColumn prefWidth="75.0" styleClass="montetary-value" text=">5yr" />
|
||||
</columns>
|
||||
<columnResizePolicy>
|
||||
<TreeTableView fx:constant="CONSTRAINED_RESIZE_POLICY" />
|
||||
</columnResizePolicy>
|
||||
</TreeTableView>
|
||||
<Label alignment="BOTTOM_LEFT" prefHeight="16.0" text="Total 15 matching issuer(s)" wrapText="true">
|
||||
<VBox.margin>
|
||||
<Insets bottom="5.0" right="10.0" top="5.0" />
|
||||
</VBox.margin>
|
||||
</Label>
|
||||
</children>
|
||||
<padding>
|
||||
<Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
|
||||
</padding>
|
||||
</VBox>
|
@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.TextField?>
|
||||
<?import javafx.scene.image.Image?>
|
||||
<?import javafx.scene.image.ImageView?>
|
||||
<?import javafx.scene.layout.StackPane?>
|
||||
|
||||
<StackPane alignment="CENTER_RIGHT" xmlns="http://javafx.com/javafx/8.0.76-ea" xmlns:fx="http://javafx.com/fxml/1">
|
||||
<children>
|
||||
<TextField fx:id="SearchCriteriaTextField" promptText="Set prompt text" styleClass="search">
|
||||
<opaqueInsets>
|
||||
<Insets />
|
||||
</opaqueInsets>
|
||||
<StackPane.margin>
|
||||
<Insets />
|
||||
</StackPane.margin>
|
||||
</TextField>
|
||||
<ImageView fitHeight="16.0" fitWidth="16.0" pickOnBounds="true" preserveRatio="true" styleClass="search-clear" StackPane.alignment="CENTER_RIGHT">
|
||||
<image>
|
||||
<Image url="@../../images/clear_inactive.png" />
|
||||
</image>
|
||||
<StackPane.margin>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</StackPane.margin>
|
||||
</ImageView>
|
||||
</children>
|
||||
</StackPane>
|
@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.TextField?>
|
||||
<?import javafx.scene.image.Image?>
|
||||
<?import javafx.scene.image.ImageView?>
|
||||
<?import javafx.scene.layout.StackPane?>
|
||||
|
||||
<StackPane alignment="CENTER_RIGHT" xmlns="http://javafx.com/javafx/8.0.76-ea" xmlns:fx="http://javafx.com/fxml/1">
|
||||
<children>
|
||||
<TextField fx:id="SearchCriteriaTextField" promptText="Set prompt text" styleClass="search">
|
||||
<opaqueInsets>
|
||||
<Insets />
|
||||
</opaqueInsets>
|
||||
<StackPane.margin>
|
||||
<Insets />
|
||||
</StackPane.margin>
|
||||
</TextField>
|
||||
<ImageView fitHeight="16.0" fitWidth="16.0" pickOnBounds="true" preserveRatio="true" styleClass="search-clear" StackPane.alignment="CENTER_RIGHT">
|
||||
<image>
|
||||
<Image url="@../../images/clear_inactive.png" />
|
||||
</image>
|
||||
<StackPane.margin>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</StackPane.margin>
|
||||
</ImageView>
|
||||
</children>
|
||||
</StackPane>
|
@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.scene.control.ButtonType?>
|
||||
<?import javafx.scene.control.ChoiceBox?>
|
||||
<?import javafx.scene.control.DialogPane?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.layout.AnchorPane?>
|
||||
|
||||
|
||||
<DialogPane expanded="true" headerText="Settings" scaleShape="false" xmlns="http://javafx.com/javafx/8.0.60" xmlns:fx="http://javafx.com/fxml/1">
|
||||
<content>
|
||||
<AnchorPane>
|
||||
<children>
|
||||
<Label layoutX="6.0" layoutY="6.0" text="We are" />
|
||||
<ChoiceBox layoutX="146.0" layoutY="2.0" prefHeight="24.0" prefWidth="360.0" AnchorPane.leftAnchor="146.0" />
|
||||
<Label layoutX="6.0" layoutY="37.0" text="Reporting currency" />
|
||||
<ChoiceBox layoutX="156.0" layoutY="33.0" prefWidth="150.0" />
|
||||
</children>
|
||||
</AnchorPane>
|
||||
</content>
|
||||
<buttonTypes>
|
||||
<ButtonType fx:constant="APPLY" />
|
||||
<ButtonType fx:constant="CLOSE" />
|
||||
</buttonTypes>
|
||||
</DialogPane>
|
@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import java.lang.String?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.ButtonType?>
|
||||
<?import javafx.scene.control.DialogPane?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.control.TableColumn?>
|
||||
<?import javafx.scene.control.TableView?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
|
||||
|
||||
<DialogPane headerText="Issuer C039201 HSBC GROUP PLC" xmlns="http://javafx.com/javafx/8.0.60" xmlns:fx="http://javafx.com/fxml/1">
|
||||
<content>
|
||||
<VBox spacing="5.0">
|
||||
<children>
|
||||
<Label styleClass="subline" text="Total 18 position(s)" />
|
||||
<Label styleClass="headline" text="USD 394.6k" />
|
||||
<TableView prefHeight="284.0" prefWidth="417.0">
|
||||
<columns>
|
||||
<TableColumn prefWidth="155.0" text="Position ID" />
|
||||
<TableColumn prefWidth="123.0" styleClass="first-column" text="Local ccy" />
|
||||
<TableColumn prefWidth="79.0" text="Amount">
|
||||
<styleClass>
|
||||
<String fx:value="second-column" />
|
||||
<String fx:value="monetary-value" />
|
||||
</styleClass>
|
||||
</TableColumn>
|
||||
<TableColumn prefWidth="75.0" styleClass="monetary-value" text="USD equiv" />
|
||||
</columns>
|
||||
<columnResizePolicy>
|
||||
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY" />
|
||||
</columnResizePolicy>
|
||||
</TableView>
|
||||
</children>
|
||||
<opaqueInsets>
|
||||
<Insets />
|
||||
</opaqueInsets>
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</padding>
|
||||
</VBox>
|
||||
</content>
|
||||
<buttonTypes>
|
||||
<ButtonType fx:constant="CLOSE" />
|
||||
</buttonTypes>
|
||||
</DialogPane>
|
@ -7,3 +7,4 @@ include 'client'
|
||||
include 'experimental'
|
||||
include 'test-utils'
|
||||
include 'network-simulator'
|
||||
include 'explorer'
|
||||
|