mirror of
https://github.com/corda/corda.git
synced 2024-12-28 16:58:55 +00:00
Address PR comments. Add links from FlowTriage view to Network and to TransactionView with showing
relevant transactions/peers on the map.
This commit is contained in:
parent
67a417389b
commit
4f00bad908
@ -7,7 +7,6 @@ import javafx.collections.FXCollections
|
||||
import net.corda.client.jfx.utils.LeftOuterJoinedMap
|
||||
import net.corda.client.jfx.utils.fold
|
||||
import net.corda.client.jfx.utils.getObservableValues
|
||||
import net.corda.client.jfx.utils.lift
|
||||
import net.corda.client.jfx.utils.recordAsAssociation
|
||||
import net.corda.core.ErrorOr
|
||||
import net.corda.core.flows.FlowInitiator
|
||||
|
@ -1,14 +1,26 @@
|
||||
package net.corda.explorer.formatters
|
||||
|
||||
import de.jensd.fx.glyphs.fontawesome.FontAwesomeIcon
|
||||
import net.corda.core.flows.FlowInitiator
|
||||
|
||||
object FlowInitiatorFormatter : Formatter<FlowInitiator> {
|
||||
override fun format(value: FlowInitiator): String {
|
||||
return when (value) {
|
||||
is FlowInitiator.Scheduled -> "Started by scheduled state:: " + value.scheduledState.ref.toString() // TODO How do we want to format that?
|
||||
is FlowInitiator.Shell -> "Started via shell"
|
||||
is FlowInitiator.Peer -> "Peer legal name: " + PartyNameFormatter.short.format(value.party.name)
|
||||
is FlowInitiator.RPC -> "Rpc username: " + value.username
|
||||
is FlowInitiator.Scheduled -> value.scheduledState.ref.toString() // TODO How do we want to format that?
|
||||
is FlowInitiator.Shell -> "Shell" // TODO We don't have much information about that user.
|
||||
is FlowInitiator.Peer -> PartyNameFormatter.short.format(value.party.name)
|
||||
is FlowInitiator.RPC -> value.username
|
||||
}
|
||||
}
|
||||
|
||||
fun withIcon(value: FlowInitiator): Pair<FontAwesomeIcon, String> {
|
||||
val text = format(value)
|
||||
return when (value) {
|
||||
is FlowInitiator.Scheduled -> Pair(FontAwesomeIcon.CALENDAR, text)
|
||||
is FlowInitiator.Shell -> Pair(FontAwesomeIcon.TERMINAL, text)
|
||||
is FlowInitiator.Peer -> Pair(FontAwesomeIcon.GROUP, text)
|
||||
is FlowInitiator.RPC -> Pair(FontAwesomeIcon.SHARE, text)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,13 @@
|
||||
package net.corda.explorer.formatters
|
||||
|
||||
import org.apache.commons.lang.StringUtils.splitByCharacterTypeCamelCase
|
||||
|
||||
object FlowNameFormatter {
|
||||
val boring = object : Formatter<String> {
|
||||
override fun format(value: String) = value.split('.').last().replace("$", ": ") // TODO Better handling of names.
|
||||
val camelCase = object : Formatter<String> {
|
||||
override fun format(value: String): String {
|
||||
val flowName = value.split('.', '$').last()
|
||||
val split = splitByCharacterTypeCamelCase(flowName).filter { it.compareTo("Flow", true) != 0 } .joinToString(" ")
|
||||
return split
|
||||
}
|
||||
}
|
||||
}
|
@ -195,6 +195,7 @@ fun identicon(secureHash: SecureHash, size: Double): ImageView {
|
||||
return ImageView(IdenticonRenderer.getIdenticon(secureHash)).apply {
|
||||
isPreserveRatio = true
|
||||
fitWidth = size
|
||||
styleClass += "identicon"
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@ import javafx.animation.FadeTransition
|
||||
import javafx.animation.TranslateTransition
|
||||
import javafx.beans.binding.Bindings
|
||||
import javafx.beans.property.SimpleObjectProperty
|
||||
import javafx.beans.value.ObservableValue
|
||||
import javafx.collections.FXCollections
|
||||
import javafx.geometry.Bounds
|
||||
import javafx.geometry.Point2D
|
||||
@ -41,6 +42,9 @@ class Network : CordaView() {
|
||||
val notaries by observableList(NetworkIdentityModel::notaries)
|
||||
val peers by observableList(NetworkIdentityModel::parties)
|
||||
val transactions by observableList(TransactionDataModel::partiallyResolvedTransactions)
|
||||
var centralPeer: String? = null
|
||||
private var centralLabel: ObservableValue<Label?>
|
||||
|
||||
// UI components
|
||||
private val myIdentityPane by fxid<BorderPane>()
|
||||
private val notaryList by fxid<VBox>()
|
||||
@ -114,7 +118,17 @@ class Network : CordaView() {
|
||||
return MapViewComponents(this, button, mapLabel)
|
||||
}
|
||||
|
||||
override fun onDock() {
|
||||
centralLabel = mapLabels.firstOrDefault(myMapLabel, { centralPeer?.contains(it.text, true) ?: false })
|
||||
}
|
||||
|
||||
override fun onUndock() {
|
||||
centralPeer = null
|
||||
centralLabel = myMapLabel
|
||||
}
|
||||
|
||||
init {
|
||||
centralLabel = mapLabels.firstOrDefault(myMapLabel, { centralPeer?.contains(it.text, true) ?: false })
|
||||
myIdentityPane.centerProperty().bind(myButton)
|
||||
Bindings.bindContent(notaryList.children, notaryButtons)
|
||||
Bindings.bindContent(peerList.children, peerButtons)
|
||||
@ -122,7 +136,7 @@ class Network : CordaView() {
|
||||
// Run once when the screen is ready.
|
||||
// TODO : Find a better way to do this.
|
||||
mapPane.heightProperty().addListener { _, old, _ ->
|
||||
if (old == 0.0) myMapLabel.value?.let { mapScrollPane.centerLabel(it) }
|
||||
if (old == 0.0) centralLabel.value?.let { mapScrollPane.centerLabel(it) }
|
||||
}
|
||||
// Listen on zooming gesture, if device has gesture support.
|
||||
mapPane.setOnZoom { zoom(it.zoomFactor, Point2D(it.x, it.y)) }
|
||||
@ -142,6 +156,7 @@ class Network : CordaView() {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO It doesn't work as expected.
|
||||
private fun ScrollPane.centerLabel(label: Label) {
|
||||
this.hvalue = (label.boundsInParent.width / 2 + label.boundsInParent.minX) / mapImageView.layoutBounds.width
|
||||
this.vvalue = (label.boundsInParent.height / 2 + label.boundsInParent.minY) / mapImageView.layoutBounds.height
|
||||
|
@ -103,43 +103,29 @@ class StateMachineViewer : CordaView("Flow Triage") {
|
||||
minWidth = 100.0
|
||||
maxWidth = 200.0
|
||||
}.setCustomCellFactory {
|
||||
label("$it") {
|
||||
val toDisplay = it.toString().removeSurrounding("[", "]")
|
||||
label(toDisplay) {
|
||||
val hash = SecureHash.sha256(it.toString())
|
||||
graphic = identicon(hash, 15.0)
|
||||
tooltip = identiconToolTip(hash, it.toString())
|
||||
tooltip = identiconToolTip(hash, toDisplay)
|
||||
}
|
||||
}
|
||||
column("Flow name", StateMachineData::stateMachineName).cellFormat { text = FlowNameFormatter.boring.format(it) }
|
||||
column("Initiator", StateMachineData::flowInitiator).cellFormat { text = FlowInitiatorFormatter.format(it) }
|
||||
column("Flow Status", StateMachineData::stateMachineStatus).cellFormat { text = it.status ?: "No progress data" }
|
||||
column("Result", StateMachineData::addRmStatus).setCustomCellFactory {
|
||||
if (it is StateMachineStatus.Removed) {
|
||||
if (it.result.error == null) {
|
||||
label("Success") {
|
||||
graphic = FontAwesomeIconView(FontAwesomeIcon.CHECK).apply {
|
||||
glyphSize = 15.0
|
||||
textAlignment = TextAlignment.CENTER
|
||||
style = "-fx-fill: green"
|
||||
}
|
||||
}
|
||||
column("Flow name", StateMachineData::stateMachineName).cellFormat { text = FlowNameFormatter.camelCase.format(it) }
|
||||
column("Initiator", StateMachineData::flowInitiator).setCustomCellFactory {
|
||||
val (initIcon, initText) = FlowInitiatorFormatter.withIcon(it)
|
||||
label { makeIconLabel(this, initIcon, initText, "-fx-fill: lightgray") }
|
||||
}
|
||||
column("Flow Status", StateMachineData::smmStatus).setCustomCellFactory {
|
||||
val addRm = it.first.value
|
||||
val progress = it.second.value.status ?: "No progress data"
|
||||
if (addRm is StateMachineStatus.Removed) {
|
||||
if (addRm.result.error == null) {
|
||||
label { makeIconLabel(this, FontAwesomeIcon.CHECK, "Success", "-fx-fill: green") }
|
||||
} else {
|
||||
label("Error") {
|
||||
graphic = FontAwesomeIconView(FontAwesomeIcon.BOLT).apply {
|
||||
glyphSize = 15.0
|
||||
textAlignment = TextAlignment.CENTER
|
||||
style = "-fx-fill: -color-4"
|
||||
}
|
||||
}
|
||||
label { makeIconLabel(this, FontAwesomeIcon.BOLT, progress, "-fx-fill: -color-4") }
|
||||
}
|
||||
} else {
|
||||
label("In progress") {
|
||||
graphic = FontAwesomeIconView(FontAwesomeIcon.ROCKET).apply {
|
||||
// Blazing fast! Try not to blink.
|
||||
glyphSize = 15.0
|
||||
textAlignment = TextAlignment.CENTER
|
||||
style = "-fx-fill: lightslategrey"
|
||||
}
|
||||
}
|
||||
label { makeIconLabel(this, FontAwesomeIcon.ROCKET, progress, "-fx-fill: lightslategrey") }
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -150,22 +136,22 @@ class StateMachineViewer : CordaView("Flow Triage") {
|
||||
"Flow name" to { sm, s -> sm.stateMachineName.contains(s, true) },
|
||||
"Initiator" to { sm, s -> FlowInitiatorFormatter.format(sm.flowInitiator).contains(s, true) },
|
||||
"Flow Status" to { sm, s ->
|
||||
val stat = sm.stateMachineStatus.value.status ?: "No progress data"
|
||||
val stat = sm.smmStatus.second.value?.status ?: "No progress data"
|
||||
stat.contains(s, true)
|
||||
},
|
||||
"Error" to { sm, _ ->
|
||||
val smAddRm = sm.addRmStatus.value
|
||||
val smAddRm = sm.smmStatus.first.value
|
||||
if (smAddRm is StateMachineStatus.Removed)
|
||||
smAddRm.result.error != null
|
||||
else false
|
||||
},
|
||||
"Done" to { sm, _ ->
|
||||
val smAddRm = sm.addRmStatus.value
|
||||
val smAddRm = sm.smmStatus.first.value
|
||||
if (smAddRm is StateMachineStatus.Removed)
|
||||
smAddRm.result.error == null
|
||||
else false
|
||||
},
|
||||
"In progress" to { sm, _ -> sm.addRmStatus.value !is StateMachineStatus.Removed },
|
||||
"In progress" to { sm, _ -> sm.smmStatus.first.value !is StateMachineStatus.Removed },
|
||||
disabledFields = listOf("Error", "Done", "In progress")
|
||||
)
|
||||
root.top = searchField.root
|
||||
@ -175,31 +161,20 @@ class StateMachineViewer : CordaView("Flow Triage") {
|
||||
})
|
||||
}
|
||||
|
||||
private inner class StateMachineDetailsView(val smmData: StateMachineData) : Fragment() {
|
||||
private inner class StateMachineDetailsView(smmData: StateMachineData) : Fragment() {
|
||||
override val root by fxml<Parent>()
|
||||
private val flowNameLabel by fxid<Label>()
|
||||
private val flowProgressVBox by fxid<VBox>()
|
||||
private val flowInitiatorGrid by fxid<GridPane>()
|
||||
private val flowInitiatorTitle by fxid<Label>()
|
||||
private val flowResultVBox by fxid<VBox>()
|
||||
|
||||
init {
|
||||
flowNameLabel.apply {
|
||||
text = FlowNameFormatter.boring.format(smmData.stateMachineName)
|
||||
}
|
||||
//TODO It would be nice to have flow graph with showing progress steps with subflows + timestamps (left it for second iteration).
|
||||
flowProgressVBox.apply {
|
||||
label {
|
||||
text = smmData.stateMachineStatus.value?.status ?: "No progress data"
|
||||
}
|
||||
}
|
||||
when (smmData.flowInitiator) {
|
||||
is FlowInitiator.Shell -> makeShellGrid(flowInitiatorGrid, flowInitiatorTitle) // TODO Extend this when we will have more information on shell user.
|
||||
is FlowInitiator.Peer -> makePeerGrid(flowInitiatorGrid, flowInitiatorTitle, smmData.flowInitiator as FlowInitiator.Peer)
|
||||
is FlowInitiator.RPC -> makeRPCGrid(flowInitiatorGrid, flowInitiatorTitle, smmData.flowInitiator as FlowInitiator.RPC)
|
||||
is FlowInitiator.Scheduled -> makeScheduledGrid(flowInitiatorGrid, flowInitiatorTitle, smmData.flowInitiator as FlowInitiator.Scheduled)
|
||||
is FlowInitiator.Shell -> makeShellGrid(flowInitiatorGrid) // TODO Extend this when we will have more information on shell user.
|
||||
is FlowInitiator.Peer -> makePeerGrid(flowInitiatorGrid, smmData.flowInitiator as FlowInitiator.Peer)
|
||||
is FlowInitiator.RPC -> makeRPCGrid(flowInitiatorGrid, smmData.flowInitiator as FlowInitiator.RPC)
|
||||
is FlowInitiator.Scheduled -> makeScheduledGrid(flowInitiatorGrid, smmData.flowInitiator as FlowInitiator.Scheduled)
|
||||
}
|
||||
val status = smmData.addRmStatus.value
|
||||
val status = smmData.smmStatus.first.value
|
||||
if (status is StateMachineStatus.Removed) {
|
||||
status.result.match(onValue = { makeResultVBox(flowResultVBox, it) }, onError = { makeErrorVBox(flowResultVBox, it) })
|
||||
}
|
||||
@ -207,22 +182,23 @@ class StateMachineViewer : CordaView("Flow Triage") {
|
||||
}
|
||||
|
||||
private fun <T> makeResultVBox(vbox: VBox, result: T) {
|
||||
if (result == null) {
|
||||
vbox.apply { label("No return value from flow.").apply { style { fontWeight = FontWeight.BOLD } } }
|
||||
} else if (result is SignedTransaction) {
|
||||
// TODO Make link to transaction view
|
||||
if (result is SignedTransaction) {
|
||||
vbox.apply {
|
||||
label("Signed transaction").apply { style { fontWeight = FontWeight.BOLD } }
|
||||
label {
|
||||
style = "-fx-cursor: hand;"
|
||||
setOnMouseClicked {
|
||||
if (it.button == MouseButton.PRIMARY) {
|
||||
selectedView.value = tornadofx.find<TransactionViewer>().apply { txIdToScroll = result.id }
|
||||
}
|
||||
}
|
||||
text = result.id.toString()
|
||||
graphic = identicon(result.id, 30.0)
|
||||
tooltip = identiconToolTip(result.id)
|
||||
}
|
||||
|
||||
}
|
||||
} else if (result is Unit) {
|
||||
vbox.apply { label("Flow completed with success.").apply { style { fontWeight = FontWeight.BOLD } } }
|
||||
} else {
|
||||
} else if (result != null && result !is Unit) {
|
||||
// TODO Here we could have sth different than SignedTransaction/Unit
|
||||
vbox.apply {
|
||||
label("Flow completed with success. Result: ").apply { style { fontWeight = FontWeight.BOLD } }
|
||||
@ -233,36 +209,35 @@ class StateMachineViewer : CordaView("Flow Triage") {
|
||||
|
||||
private fun makeErrorVBox(vbox: VBox, error: Throwable) {
|
||||
vbox.apply {
|
||||
label("Error") {
|
||||
label {
|
||||
text = error::class.simpleName
|
||||
graphic = FontAwesomeIconView(FontAwesomeIcon.BOLT).apply {
|
||||
glyphSize = 30
|
||||
textAlignment = TextAlignment.CENTER
|
||||
style = "-fx-fill: -color-4"
|
||||
}
|
||||
}
|
||||
}
|
||||
vbox.apply {
|
||||
vbox {
|
||||
spacing = 10.0
|
||||
label { text = error::class.simpleName }
|
||||
label { text = error.message }
|
||||
}
|
||||
label { text = error.message }
|
||||
}
|
||||
}
|
||||
|
||||
private fun makeShellGrid(gridPane: GridPane, title: Label) {
|
||||
title.apply {
|
||||
text = "Flow started by shell user"
|
||||
}
|
||||
}
|
||||
|
||||
private fun makePeerGrid(gridPane: GridPane, title: Label, initiator: FlowInitiator.Peer) {
|
||||
title.apply {
|
||||
text = "Flow started by a peer node"
|
||||
}
|
||||
private fun makeShellGrid(gridPane: GridPane) {
|
||||
gridPane.apply {
|
||||
label("Flow started by shell user")
|
||||
}
|
||||
}
|
||||
|
||||
private fun makePeerGrid(gridPane: GridPane, initiator: FlowInitiator.Peer) {
|
||||
gridPane.apply {
|
||||
style = "-fx-cursor: hand;"
|
||||
setOnMouseClicked {
|
||||
if (it.button == MouseButton.PRIMARY) {
|
||||
val short = PartyNameFormatter.short.format(initiator.party.name)
|
||||
selectedView.value = tornadofx.find<Network>().apply { centralPeer = short}
|
||||
}
|
||||
}
|
||||
row {
|
||||
label("Legal name: ") {
|
||||
label("Peer legal name: ") {
|
||||
gridpaneConstraints { hAlignment = HPos.LEFT }
|
||||
style { fontWeight = FontWeight.BOLD }
|
||||
minWidth = 150.0
|
||||
@ -282,13 +257,10 @@ class StateMachineViewer : CordaView("Flow Triage") {
|
||||
}
|
||||
}
|
||||
|
||||
private fun makeRPCGrid(gridPane: GridPane, title: Label, initiator: FlowInitiator.RPC) {
|
||||
title.apply {
|
||||
text = "Flow started by a RPC user"
|
||||
}
|
||||
private fun makeRPCGrid(gridPane: GridPane, initiator: FlowInitiator.RPC) {
|
||||
gridPane.apply {
|
||||
row {
|
||||
label("User name: ") {
|
||||
label("RPC user name: ") {
|
||||
gridpaneConstraints { hAlignment = HPos.LEFT }
|
||||
style { fontWeight = FontWeight.BOLD }
|
||||
prefWidth = 150.0
|
||||
@ -299,10 +271,7 @@ class StateMachineViewer : CordaView("Flow Triage") {
|
||||
}
|
||||
|
||||
// TODO test
|
||||
private fun makeScheduledGrid(gridPane: GridPane, title: Label, initiator: FlowInitiator.Scheduled) {
|
||||
title.apply {
|
||||
text = "Flow started as scheduled activity"
|
||||
}
|
||||
private fun makeScheduledGrid(gridPane: GridPane, initiator: FlowInitiator.Scheduled) {
|
||||
gridPane.apply {
|
||||
row {
|
||||
label("Scheduled state: ") {
|
||||
|
@ -76,6 +76,24 @@ class TransactionViewer : CordaView("Transactions") {
|
||||
|
||||
data class Inputs(val resolved: ObservableList<StateAndRef<ContractState>>, val unresolved: ObservableList<StateRef>)
|
||||
|
||||
override fun onDock() {
|
||||
txIdToScroll?.let {
|
||||
scrollPosition = transactionViewTable.items.indexOfFirst { it.id == txIdToScroll }
|
||||
if (scrollPosition > 0) {
|
||||
expander.toggleExpanded(scrollPosition)
|
||||
val tx = transactionViewTable.items[scrollPosition]
|
||||
transactionViewTable.scrollTo(tx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onUndock() {
|
||||
val isExpanded = expander.getExpandedProperty(transactionViewTable.items[scrollPosition]) // It is evil.
|
||||
if (isExpanded.value) expander.toggleExpanded(scrollPosition)
|
||||
scrollPosition = 0
|
||||
txIdToScroll = null
|
||||
}
|
||||
|
||||
/**
|
||||
* We map the gathered data about transactions almost one-to-one to the nodes.
|
||||
*/
|
||||
@ -162,7 +180,7 @@ class TransactionViewer : CordaView("Transactions") {
|
||||
titleProperty.bind(reportingCurrency.map { "Total value ($it equiv)" })
|
||||
}
|
||||
|
||||
rowExpander {
|
||||
expander = rowExpander {
|
||||
add(ContractStatesView(it).root)
|
||||
prefHeight = 400.0
|
||||
}.apply {
|
||||
@ -194,6 +212,8 @@ class TransactionViewer : CordaView("Transactions") {
|
||||
init {
|
||||
right {
|
||||
label {
|
||||
val hash = SecureHash.randomSHA256()
|
||||
graphic = identicon(hash, 30.0)
|
||||
textProperty().bind(Bindings.size(partiallyResolvedTransactions).map(Number::toString))
|
||||
BorderPane.setAlignment(this, Pos.BOTTOM_RIGHT)
|
||||
}
|
||||
|
@ -19,10 +19,10 @@
|
||||
<Insets bottom="25" left="5" right="5" top="5"/>
|
||||
</StackPane.margin>
|
||||
<TitledPane styleClass="networkTile" text="My Identity">
|
||||
<BorderPane fx:id="myIdentityPane" minHeight="150"/>
|
||||
<BorderPane fx:id="myIdentityPane" minHeight="150" maxWidth="Infinity"/>
|
||||
</TitledPane>
|
||||
<TitledPane styleClass="networkTile" text="Notaries">
|
||||
<BorderPane minHeight="150">
|
||||
<BorderPane minHeight="150" maxWidth="Infinity">
|
||||
<center>
|
||||
<ScrollPane hbarPolicy="NEVER">
|
||||
<VBox fx:id="notaryList" maxWidth="-Infinity"/>
|
||||
@ -31,7 +31,7 @@
|
||||
</BorderPane>
|
||||
</TitledPane>
|
||||
<TitledPane styleClass="networkTile" text="Peers" VBox.vgrow="ALWAYS">
|
||||
<BorderPane minHeight="150">
|
||||
<BorderPane minHeight="150" maxWidth="Infinity">
|
||||
<center>
|
||||
<ScrollPane hbarPolicy="NEVER">
|
||||
<VBox fx:id="peerList" maxWidth="-Infinity">
|
||||
|
@ -1,48 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.control.ScrollPane?>
|
||||
<?import javafx.scene.control.TitledPane?>
|
||||
<?import javafx.scene.layout.ColumnConstraints?>
|
||||
<?import javafx.scene.layout.GridPane?>
|
||||
<?import javafx.scene.layout.RowConstraints?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
|
||||
<GridPane styleClass="smm-detail-grid" stylesheets="@../css/corda.css" xmlns="http://javafx.com/javafx/8.0.112" xmlns:fx="http://javafx.com/fxml/1">
|
||||
<GridPane styleClass="flow-expanded" stylesheets="@../css/corda.css" xmlns="http://javafx.com/javafx/8.0.112" xmlns:fx="http://javafx.com/fxml/1">
|
||||
<padding>
|
||||
<Insets bottom="5" left="5" right="5" top="5" />
|
||||
</padding>
|
||||
<TitledPane fx:id="flowNamePane" collapsible="false" text="Flow name" GridPane.columnSpan="2" GridPane.fillWidth="true" GridPane.rowIndex="0">
|
||||
<Label fx:id="flowNameLabel" style="-fx-font-weight: bold"/>
|
||||
</TitledPane>
|
||||
<TitledPane fx:id="flowInitiatorPane" collapsible="false" maxHeight="Infinity" text="Flow initiator" GridPane.columnIndex="0" GridPane.fillWidth="true" GridPane.rowIndex="1">
|
||||
<ScrollPane maxHeight="Infinity" minHeight="120">
|
||||
<GridPane fx:id="flowInitiatorGrid" hgap="10.0" vgap="10.0">
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="5.0" right="10.0" top="10.0" />
|
||||
</padding>
|
||||
<Label fx:id="flowInitiatorTitle" GridPane.columnIndex="0" GridPane.columnSpan="2" GridPane.halignment="LEFT" GridPane.rowIndex="0" style="-fx-font-weight: bold"/>
|
||||
</GridPane>
|
||||
</ScrollPane>
|
||||
</TitledPane>
|
||||
<TitledPane fx:id="flowResultPane" collapsible="false" maxHeight="Infinity" text="Result" GridPane.columnIndex="1" GridPane.rowIndex="1">
|
||||
<ScrollPane maxHeight="Infinity">
|
||||
<VBox fx:id="flowResultVBox" spacing="10.0">
|
||||
<padding>
|
||||
<Insets bottom="5" left="5" right="5" top="5" />
|
||||
</padding>
|
||||
</VBox>
|
||||
</ScrollPane>
|
||||
</TitledPane>
|
||||
<TitledPane fx:id="flowProgressPane" collapsible="false" text="Progress steps" GridPane.columnSpan="2" GridPane.rowIndex="2">
|
||||
<!--TODO add progress graph-->
|
||||
<VBox fx:id="flowProgressVBox" spacing="10.0">
|
||||
<padding>
|
||||
<Insets bottom="5" left="5" right="5" top="5" />
|
||||
</padding>
|
||||
</VBox>
|
||||
</TitledPane>
|
||||
<GridPane fx:id="flowInitiatorGrid" hgap="10.0" vgap="10.0" maxHeight="Infinity" maxWidth="Infinity" GridPane.fillWidth="true" GridPane.rowIndex="0">
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="5.0" right="10.0" />
|
||||
</padding>
|
||||
</GridPane>
|
||||
<VBox fx:id="flowResultVBox" spacing="10.0" maxHeight="Infinity" maxWidth="Infinity" GridPane.rowIndex="1" GridPane.fillWidth="true">
|
||||
<padding>
|
||||
<Insets bottom="5" left="5" right="5" top="5" />
|
||||
</padding>
|
||||
</VBox>
|
||||
<columnConstraints>
|
||||
<ColumnConstraints minWidth="450.0" />
|
||||
<ColumnConstraints hgrow="ALWAYS" />
|
||||
@ -50,6 +27,5 @@
|
||||
<rowConstraints>
|
||||
<RowConstraints vgrow="ALWAYS" />
|
||||
<RowConstraints vgrow="ALWAYS" />
|
||||
<RowConstraints vgrow="ALWAYS" />
|
||||
</rowConstraints>
|
||||
</GridPane>
|
||||
|
Loading…
Reference in New Issue
Block a user