DemoBench: better autocomplete and flags for city list

This commit is contained in:
Mike Hearn 2017-04-14 00:53:49 +02:00
parent dec2c82693
commit 3f7f05a0d9
249 changed files with 61 additions and 133 deletions

View File

@ -59,7 +59,7 @@ object CityDatabase {
if (line.startsWith("#")) continue
val (name, lng, lat) = line.split('\t')
val matchResult = matcher.matchEntire(name) ?: throw Exception("Could not parse line: $line")
val (city, country) = matchResult!!.destructured
val (city, country) = matchResult.destructured
val location = PhysicalLocation(WorldCoordinate(lat.toDouble(), lng.toDouble()), city, country)
caseInsensitiveLookups[city.toLowerCase()] = location
cityMap[city] = location

View File

@ -484,7 +484,6 @@ San Miguel De Tucuman (AR) -65.22 -26.82
Changwat Phra Nakhon Si Ayutthaya (TH) 100.53 14.33
Selam (IN) 78.17 11.65
Taroudannt (MA) -8.87 30.47
Reunion (RE) 55.52 -20.87
Tiruchchirappalli (IN) 78.68 10.8
Hims (SY) 36.72 34.72
Hohhot (CN) 111.65 40.8

View File

@ -1,5 +1,5 @@
buildscript {
ext.tornadofx_version = '1.6.2'
ext.tornadofx_version = '1.7.1'
ext.jna_version = '4.1.0'
ext.purejavacomm_version = '0.0.17'
ext.controlsfx_version = '8.40.12'
@ -33,10 +33,15 @@ repositories {
dirs 'libs'
}
maven {
jcenter()
url 'http://www.sparetimelabs.com/maven2'
}
}
compileKotlin {
kotlinOptions.jvmTarget= "1.8"
}
dependencies {
// TornadoFX: A lightweight Kotlin framework for working with JavaFX UI's.
compile "no.tornado:tornadofx:$tornadofx_version"
@ -57,6 +62,9 @@ dependencies {
compile "org.apache.logging.log4j:log4j-core:$log4j_version"
compile "com.typesafe:config:$typesafe_config_version"
// FontAwesomeFX: icons in the form of a font.
compile "de.jensd:fontawesomefx-fontawesome:4.7.0-5"
// These libraries don't exist in any Maven repository I can find.
// See: https://github.com/JetBrains/jediterm
//

View File

@ -55,7 +55,7 @@ class NodeController(check: atRuntime = ::checkExists) : Controller() {
nodeData.legalName.value.trim(),
nodeData.p2pPort.value,
nodeData.rpcPort.value,
nodeData.nearestCity.value.trim(),
nodeData.nearestCity.value.description.trim(),
nodeData.webPort.value,
nodeData.h2Port.value,
nodeData.extraServices.value

View File

@ -2,13 +2,15 @@ package net.corda.demobench.model
import javafx.beans.property.SimpleIntegerProperty
import javafx.beans.property.SimpleListProperty
import javafx.beans.property.SimpleObjectProperty
import javafx.beans.property.SimpleStringProperty
import net.corda.core.node.CityDatabase
import tornadofx.*
class NodeData {
val legalName = SimpleStringProperty("")
val nearestCity = SimpleStringProperty("London")
val nearestCity = SimpleObjectProperty(CityDatabase["London"]!!)
val p2pPort = SimpleIntegerProperty()
val rpcPort = SimpleIntegerProperty()
val webPort = SimpleIntegerProperty()

View File

@ -1,27 +1,30 @@
package net.corda.demobench.views
import de.jensd.fx.glyphs.fontawesome.FontAwesomeIcon
import de.jensd.fx.glyphs.fontawesome.FontAwesomeIconView
import de.jensd.fx.glyphs.fontawesome.utils.FontAwesomeIconFactory
import javafx.application.Platform
import javafx.geometry.Pos
import javafx.scene.control.ComboBox
import javafx.scene.control.SelectionMode.MULTIPLE
import javafx.scene.control.SelectionMode.values
import javafx.scene.image.Image
import javafx.scene.image.ImageView
import javafx.scene.input.KeyCode
import javafx.scene.layout.Pane
import javafx.scene.layout.Priority
import javafx.stage.FileChooser
import javafx.util.converter.NumberStringConverter
import javafx.util.StringConverter
import net.corda.core.node.CityDatabase
import net.corda.core.node.PhysicalLocation
import net.corda.core.utilities.DUMMY_NOTARY
import net.corda.demobench.model.*
import net.corda.demobench.ui.CloseableTab
import org.controlsfx.control.textfield.TextFields
import tornadofx.*
import java.nio.file.Path
import java.text.DecimalFormat
import java.util.*
class NodeTabView : Fragment() {
private val cityList = CityDatabase.cityMap.keys.toList().sorted()
override val root = stackpane {}
private val main by inject<DemoBenchView>()
@ -29,11 +32,9 @@ class NodeTabView : Fragment() {
private companion object : Component() {
const val textWidth = 465.0
const val numberWidth = 100.0
const val maxNameLength = 15
val integerFormat = DecimalFormat()
val notNumber = "[^\\d]".toRegex()
val jvm by inject<JVMConfig>()
@ -63,13 +64,6 @@ class NodeTabView : Fragment() {
field("Nearest city") { nearestCityField() }
}
/*fieldset("Ports") {
field("P2P Port", op = { p2pPortField() })
field("RPC port", op = { rpcPortField() })
field("Web Port", op = { webPortField() })
field("Database Port", op = { databasePortField() })
}*/
hbox {
styleClass.addAll("node-panel")
@ -105,6 +99,8 @@ class NodeTabView : Fragment() {
cordapps.add(app)
}
}
FontAwesomeIconFactory.get().setIcon(this, FontAwesomeIcon.PLUS)
}
// Spacer pane.
@ -121,6 +117,7 @@ class NodeTabView : Fragment() {
main.enableSaveProfile()
}
}
graphic = FontAwesomeIconView(FontAwesomeIcon.PLAY_CIRCLE).apply { style += "-fx-fill: white" }
}
}
}
@ -133,12 +130,13 @@ class NodeTabView : Fragment() {
nodeTab.setOnCloseRequest {
nodeTerminalView.destroy()
}
nodeTab.graphic = FontAwesomeIconView(FontAwesomeIcon.BANK)
root.add(nodeConfigView)
root.add(nodeTerminalView)
model.legalName.value = if (nodeController.hasNetworkMap()) "" else DUMMY_NOTARY.name
model.nearestCity.value = if (nodeController.hasNetworkMap()) "" else "London"
model.nearestCity.value = if (nodeController.hasNetworkMap()) null else CityDatabase["London"]!!
model.p2pPort.value = nodeController.nextPort
model.rpcPort.value = nodeController.nextPort
model.webPort.value = nodeController.nextPort
@ -147,6 +145,8 @@ class NodeTabView : Fragment() {
chooser.title = "CorDapps"
chooser.initialDirectory = jvm.dataHome.toFile()
chooser.extensionFilters.add(FileChooser.ExtensionFilter("CorDapps (*.jar)", "*.jar", "*.JAR"))
model.validate(focusFirstError = true)
}
private fun Pane.nodeNameField() = textfield(model.legalName) {
@ -169,128 +169,38 @@ class NodeTabView : Fragment() {
}
}
// private fun Pane.nearestCityField() = textfield(model.nearestCity) {
// minWidth = textWidth
// TextFields.bindAutoCompletion(this, CityDatabase.cityMap.keys)
// validator(trigger = ValidationTrigger.OnBlur) {
// if (it == null || it.trim().isEmpty()) {
// error("Nearest city is required")
// } else if (CityDatabase[it] == null) {
// error("$it is not in our database, sorry")
// } else {
// null
// }
// }
// }
private fun Pane.nearestCityField() = combobox(model.nearestCity, cityList) {
minWidth = textWidth
isEditable = true
TextFields.bindAutoCompletion(editor, cityList)
validator(trigger = ValidationTrigger.OnBlur) {
if (it == null || it.trim().isEmpty()) {
error("Nearest city is required")
} else if (CityDatabase[it] == null) {
error("$it is not in our database, sorry")
} else {
null
}
}
private val flags = runAsync {
CityDatabase.cityMap.values.map { it.countryCode }.toSet().map { it to Image(resources["/net/corda/demobench/flags/$it.png"]) }.toMap()
}
private fun Pane.p2pPortField() = textfield(model.p2pPort, NumberStringConverter(integerFormat)) {
minWidth = numberWidth
validator {
if ((it == null) || it.isEmpty()) {
error("Port number required")
} else if (it.contains(notNumber)) {
error("Invalid port number")
} else {
val port = it.toInt()
if (!nodeController.isPortAvailable(port)) {
error("Port $it is unavailable")
} else if (port == model.rpcPort.value) {
error("Clashes with RPC port")
} else if (port == model.webPort.value) {
error("Clashes with web port")
} else if (port == model.h2Port.value) {
error("Clashes with database port")
} else {
null
private fun Pane.nearestCityField(): ComboBox<PhysicalLocation> {
return combobox(model.nearestCity, CityDatabase.cityMap.values.toList().sortedBy { it.description }) {
minWidth = textWidth
styleClass += "city-picker"
cellFormat {
graphic = hbox(spacing = 10) {
imageview {
image = flags.get()[it.countryCode]
}
label(it.description)
alignment = Pos.CENTER_LEFT
}
}
}
}
private fun Pane.rpcPortField() = textfield(model.rpcPort, NumberStringConverter(integerFormat)) {
minWidth = 100.0
validator {
if ((it == null) || it.isEmpty()) {
error("Port number required")
} else if (it.contains(notNumber)) {
error("Invalid port number")
} else {
val port = it.toInt()
if (!nodeController.isPortAvailable(port)) {
error("Port $it is unavailable")
} else if (port == model.p2pPort.value) {
error("Clashes with P2P port")
} else if (port == model.webPort.value) {
error("Clashes with web port")
} else if (port == model.h2Port.value) {
error("Clashes with database port")
} else {
null
}
validator {
if (it == null) error("Please select a city") else null
}
}
}
private fun Pane.webPortField() = textfield(model.webPort, NumberStringConverter(integerFormat)) {
minWidth = numberWidth
validator {
if ((it == null) || it.isEmpty()) {
error("Port number required")
} else if (it.contains(notNumber)) {
error("Invalid port number")
} else {
val port = it.toInt()
if (!nodeController.isPortAvailable(port)) {
error("Port $it is unavailable")
} else if (port == model.p2pPort.value) {
error("Clashes with P2P port")
} else if (port == model.rpcPort.value) {
error("Clashes with RPC port")
} else if (port == model.h2Port.value) {
error("Clashes with database port")
} else {
null
}
converter = object : StringConverter<PhysicalLocation>() {
override fun toString(loc: PhysicalLocation?) = loc?.description ?: ""
override fun fromString(string: String): PhysicalLocation? = CityDatabase[string]
}
}
}
private fun Pane.databasePortField() = textfield(model.h2Port, NumberStringConverter(integerFormat)) {
minWidth = numberWidth
validator {
if ((it == null) || it.isEmpty()) {
error("Port number required")
} else if (it.contains(notNumber)) {
error("Invalid port number")
} else {
val port = it.toInt()
if (!nodeController.isPortAvailable(port)) {
error("Port $it is unavailable")
} else if (port == model.p2pPort.value) {
error("Clashes with P2P port")
} else if (port == model.rpcPort.value) {
error("Clashes with RPC port")
} else if (port == model.webPort.value) {
error("Clashes with web port")
} else {
null
}
}
value = CityDatabase["London"]
// TODO: Find a way to make the popup the same width as when not auto-completing.
isEditable = true
makeAutocompletable()
}
}
@ -302,6 +212,7 @@ class NodeTabView : Fragment() {
val config = nodeController.validate(model.item)
if (config != null) {
nodeConfigView.isVisible = false
nodeTab.graphic = ImageView(flags.get()[model.nearestCity.value.countryCode]).apply { fitWidth = 24.0; isPreserveRatio = true }
config.install(cordapps)
launchNode(config)
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 824 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 850 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 935 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 961 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 826 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1017 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 850 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 991 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 772 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 994 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 913 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 893 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 853 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 873 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 888 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 862 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 835 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 982 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1008 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 984 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 761 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 884 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 979 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 746 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 849 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 778 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 902 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 903 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 906 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 837 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 918 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 960 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1008 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 878 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 940 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 942 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 982 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 993 B

Some files were not shown because too many files have changed in this diff Show More