explorer: Add explorer subproject with javafx screens for cash and transactions

This commit is contained in:
Andras Slemmer 2016-08-30 14:16:28 +01:00
parent dc162358c4
commit 1ec89e07c4
56 changed files with 2432 additions and 0 deletions

3
.idea/modules.xml generated
View File

@ -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
View 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'
}

View 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()
}
}

View 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"
}
}

View File

@ -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}"
}
}
}
}

View File

@ -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}"
}
}
}
}

View File

@ -0,0 +1,6 @@
package com.r3corda.explorer.formatters
interface Formatter<T> {
fun format(value: T): String
}

View File

@ -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)
}
}
}

View File

@ -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)
}}
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}
}
}
}

View File

@ -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)
}
}
}
}

View File

@ -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"
})
}
}

View File

@ -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())
}
}

View 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
}
}
}
}

View File

@ -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)
}
}

View File

@ -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()
}
}

View File

@ -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;
}

View 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;
}

View 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
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 485 KiB

View File

@ -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>

View File

@ -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="&gt;&gt;" />
<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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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="&gt;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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -7,3 +7,4 @@ include 'client'
include 'experimental'
include 'test-utils'
include 'network-simulator'
include 'explorer'