diff --git a/tools/explorer/src/main/kotlin/net/corda/explorer/views/Network.kt b/tools/explorer/src/main/kotlin/net/corda/explorer/views/Network.kt index dc1fc4a5fa..6adfb1af73 100644 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/views/Network.kt +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/views/Network.kt @@ -58,13 +58,11 @@ class Network : CordaView() { private val mapOriginalHeight = 2000.0 // UI node observables, declare here to create a strong ref to prevent GC, which removes listener from observables. + private var myLabel: Label? = null private val notaryComponents = notaries.map { it.render() } private val notaryButtons = notaryComponents.map { it.button } private val peerComponents = peers.map { it.render() } private val peerButtons = peerComponents.filtered { it.nodeInfo != myIdentity.value }.map { it.button } - private val myComponent = myIdentity.map { it?.render() } - private val myButton = myComponent.map { it?.button } - private val myMapLabel = myComponent.map { it?.label } private val allComponents = FXCollections.observableArrayList(notaryComponents, peerComponents).concatenate() private val allComponentMap = allComponents.associateBy { it.nodeInfo.legalIdentity } private val mapLabels = allComponents.map { it.label } @@ -87,9 +85,39 @@ class Network : CordaView() { } } + private fun NodeInfo.renderButton(mapLabel: Label): Button { + val node = this + return button { + useMaxWidth = true + graphic = vbox { + label(PartyNameFormatter.short.format(node.legalIdentity.name)) { font = Font.font(font.family, FontWeight.BOLD, 15.0) } + gridpane { + hgap = 5.0 + vgap = 5.0 + row("Pub Key :") { + copyableLabel(SimpleObjectProperty(node.legalIdentity.owningKey.toBase58String())).apply { minWidth = 400.0 } + } + row("Services :") { label(node.advertisedServices.map { it.info }.joinToString(", ")) } + node.physicalLocation?.apply { row("Location :") { label(this@apply.description) } } + } + } + setOnMouseClicked { + mapScrollPane.centerLabel(mapLabel) + } + } + } + private fun NodeInfo.render(): MapViewComponents { val node = this - val mapLabel = label(PartyNameFormatter.short.format(node.legalIdentity.name)) { + val mapLabel = label(PartyNameFormatter.short.format(node.legalIdentity.name)) + mapPane.add(mapLabel) + // applyCss: This method does not normally need to be invoked directly but may be used in conjunction with Parent.layout() + // to size a Node before the next pulse, or if the Scene is not in a Stage. + // It's needed to properly add node label to the map (before that width and height are 0 which results in wrong placement of + // nodes rendered after initial map rendering). + mapPane.applyCss() + mapPane.layout() + mapLabel.apply { graphic = FontAwesomeIconView(FontAwesomeIcon.DOT_CIRCLE_ALT) contentDisplay = ContentDisplay.TOP val coordinate = Bindings.createObjectBinding({ @@ -101,43 +129,37 @@ class Network : CordaView() { layoutYProperty().bind(coordinate.map { it.second - height / 4 }) } - val button = button { - useMaxWidth = true - graphic = vbox { - label(PartyNameFormatter.short.format(node.legalIdentity.name)) { font = Font.font(font.family, FontWeight.BOLD, 15.0) } - gridpane { - hgap = 5.0 - vgap = 5.0 - row("Full name :") { label(PartyNameFormatter.full.format(node.legalIdentity.name)) } - row("Pub Key :") { copyableLabel(SimpleObjectProperty(node.legalIdentity.owningKey.toBase58String())) } - row("Services :") { label(node.advertisedServices.map { it.info }.joinToString(", ")) } - node.physicalLocation?.apply { row("Location :") { label(this@apply.description) } } - } - } - setOnMouseClicked { mapScrollPane.centerLabel(mapLabel) } + val button = node.renderButton(mapLabel) + if (node == myIdentity.value) { + // It has to be a copy if we want to have notary both in notaries list and in identity (if we are looking at that particular notary node). + myIdentityPane.apply { center = node.renderButton(mapLabel) } + myLabel = mapLabel } return MapViewComponents(this, button, mapLabel) } override fun onDock() { - centralLabel = mapLabels.firstOrDefault(myMapLabel, { centralPeer?.contains(it.text, true) ?: false }) + centralLabel = mapLabels.firstOrDefault(SimpleObjectProperty(myLabel), { centralPeer?.contains(it.text, true) ?: false }) + centralLabel.value?.let { mapScrollPane.centerLabel(it) } } override fun onUndock() { centralPeer = null - centralLabel = myMapLabel + centralLabel = SimpleObjectProperty(myLabel) } init { - centralLabel = mapLabels.firstOrDefault(myMapLabel, { centralPeer?.contains(it.text, true) ?: false }) - myIdentityPane.centerProperty().bind(myButton) + centralLabel = mapLabels.firstOrDefault(SimpleObjectProperty(myLabel), { centralPeer?.contains(it.text, true) ?: false }) Bindings.bindContent(notaryList.children, notaryButtons) Bindings.bindContent(peerList.children, peerButtons) - Bindings.bindContent(mapPane.children, mapLabels) // Run once when the screen is ready. // TODO : Find a better way to do this. mapPane.heightProperty().addListener { _, old, _ -> - if (old == 0.0) centralLabel.value?.let { mapScrollPane.centerLabel(it) } + if (old == 0.0) centralLabel.value?.let { + mapPane.applyCss() + mapPane.layout() + mapScrollPane.centerLabel(it) + } } // Listen on zooming gesture, if device has gesture support. mapPane.setOnZoom { zoom(it.zoomFactor, Point2D(it.x, it.y)) }