Initial commit: Creating new tabs whenever the "Add Node" button is pressed. These tabs currently contain the bash shell only.

This commit is contained in:
Chris Rankin 2017-01-24 09:42:13 +00:00
parent 906dff1a35
commit 55b5a5a393
44 changed files with 450 additions and 89 deletions

View File

@ -3,6 +3,10 @@ version '0.7-SNAPSHOT'
buildscript { buildscript {
ext.kotlin_version = '1.0.6' ext.kotlin_version = '1.0.6'
ext.jna_version = '4.1.0'
ext.purejavacomm_version = '0.0.17'
ext.guava_version = '14.0.1'
ext.slf4j_version = '1.7.22'
repositories { repositories {
mavenCentral() mavenCentral()
@ -18,13 +22,32 @@ apply plugin: 'kotlin'
sourceCompatibility = 1.8 sourceCompatibility = 1.8
repositories { repositories {
flatDir {
dirs 'libs'
}
mavenLocal()
mavenCentral() mavenCentral()
maven {
url 'http://www.sparetimelabs.com/maven2'
}
} }
dependencies { dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
compile "net.java.dev.jna:jna:$jna_version"
compile "net.java.dev.jna:jna-platform:$jna_version"
compile "com.google.guava:guava:$guava_version"
compile "com.sparetimelabs:purejavacomm:$purejavacomm_version"
compile "org.slf4j:log4j-over-slf4j:$slf4j_version"
compile "org.slf4j:jul-to-slf4j:$slf4j_version"
compile "ch.qos.logback:logback-classic:1.1.3"
compile ":jediterm-terminal-2.5"
compile ':pty4j-0.7.2'
testCompile group: 'junit', name: 'junit', version: '4.11' testCompile group: 'junit', name: 'junit', version: '4.11'
// TornadoFX: A lightweight Kotlin framework for working with JavaFX UI's. // TornadoFX: A lightweight Kotlin framework for working with JavaFX UI's.
compile 'no.tornado:tornadofx:1.5.7' compile 'no.tornado:tornadofx:1.6.0'
} }

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,27 @@
package net.corda.demobench.config;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.logging.LogManager;
public class LoggingConfig {
public LoggingConfig() throws IOException {
try (InputStream input = getLoggingProperties()) {
LogManager manager = LogManager.getLogManager();
manager.readConfiguration(input);
}
}
private static InputStream getLoggingProperties() throws IOException {
InputStream input = LoggingConfig.class.getResourceAsStream("logging.properties");
if (input == null) {
File javaHome = new File(System.getProperty("java.home"));
input = new FileInputStream(new File(new File(javaHome, "lib"), "logging.properties"));
}
return input;
}
}

View File

@ -0,0 +1,41 @@
package net.corda.demobench.pty;
import com.jediterm.terminal.ProcessTtyConnector;
import com.pty4j.PtyProcess;
import com.pty4j.WinSize;
import java.nio.charset.Charset;
/**
* Copied from JediTerm pty.
* @author traff
*/
public class PtyProcessTtyConnector extends ProcessTtyConnector {
private final PtyProcess myProcess;
private final String name;
PtyProcessTtyConnector(String name, PtyProcess process, Charset charset) {
super(process, charset);
myProcess = process;
this.name = name;
}
@Override
protected void resizeImmediately() {
if (getPendingTermSize() != null && getPendingPixelSize() != null) {
myProcess.setWinSize(
new WinSize(getPendingTermSize().width, getPendingTermSize().height, getPendingPixelSize().width, getPendingPixelSize().height));
}
}
@Override
public boolean isConnected() {
return myProcess.isRunning();
}
@Override
public String getName() {
return name;
}
}

View File

@ -0,0 +1,118 @@
package net.corda.demobench.pty;
import com.jediterm.terminal.TerminalColor;
import com.jediterm.terminal.TextStyle;
import com.jediterm.terminal.TtyConnector;
import com.jediterm.terminal.ui.*;
import com.jediterm.terminal.ui.settings.DefaultSettingsProvider;
import com.jediterm.terminal.ui.settings.SettingsProvider;
import com.pty4j.PtyProcess;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import static java.nio.charset.StandardCharsets.UTF_8;
public class R3Pty implements AutoCloseable {
private static final Logger LOG = LoggerFactory.getLogger(R3Pty.class);
private final JediTermWidget terminal;
private final String name;
public R3Pty(String name, SettingsProvider settings, Dimension dimension) {
terminal = new JediTermWidget(dimension, settings);
this.name = name;
}
@Override
public void close() {
LOG.info("Closing terminal '{}'", name);
terminal.close();
}
public String getName() {
return name;
}
public JediTermWidget getTerminal() {
return terminal;
}
private TtyConnector createTtyConnector(String[] command, Map<String, String> environment, String workingDir) {
try {
PtyProcess process = PtyProcess.exec(command, environment, workingDir);
try {
return new PtyProcessTtyConnector(name, process, UTF_8);
} catch (Exception e) {
process.destroyForcibly();
process.waitFor(30, TimeUnit.SECONDS);
throw e;
}
} catch (Exception e) {
throw new IllegalStateException(e.getMessage(), e);
}
}
public void run(String[] args, Map<String, String> envs, String workingDir) {
if (terminal.isSessionRunning()) {
throw new IllegalStateException(terminal.getSessionName() + " is already running");
}
Map<String, String> environment = new HashMap<>(envs);
environment.put("TERM", "xterm");
TerminalSession session = terminal.createTerminalSession(createTtyConnector(args, environment, workingDir));
session.start();
}
public void run(String[] args, Map<String, String> envs) {
run(args, envs, null);
}
public void run(String... args) {
run(args, System.getenv());
}
public static void main(final String[] args) throws IOException {
JFrame frame = new JFrame("R3 Example");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setIconImage(ImageIO.read(R3Pty.class.getResourceAsStream("/r3logo.png")));
SettingsProvider settings = new DefaultSettingsProvider() {
@Override
public TextStyle getDefaultStyle() {
return new TextStyle(TerminalColor.WHITE, TerminalColor.BLACK);
}
@Override
public float getTerminalFontSize() {
return 20;
}
@Override
public boolean emulateX11CopyPaste() {
return true;
}
};
R3Pty pty = new R3Pty("Bungo", settings, new Dimension(164, 40));
frame.getContentPane().add(pty.getTerminal(), BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setResizable(true);
frame.setVisible(true);
pty.run(args);
}
}

View File

@ -1,6 +1,9 @@
package net.corda.demobench package net.corda.demobench
import javafx.scene.image.Image
import net.corda.demobench.views.DemoBenchView
import tornadofx.App import tornadofx.App
import tornadofx.addStageIcon
/** /**
* README! * README!
@ -33,5 +36,6 @@ import tornadofx.App
class DemoBench : App(DemoBenchView::class) { class DemoBench : App(DemoBenchView::class) {
init { init {
addStageIcon(Image("r3logo.png"))
} }
} }

View File

@ -1,13 +0,0 @@
package net.corda.demobench
import javafx.scene.Parent
import tornadofx.View
import tornadofx.importStylesheet
class DemoBenchView : View("Corda Demo Bench") {
override val root: Parent by fxml()
init {
importStylesheet("/net/corda/demobench/style.css")
}
}

View File

@ -0,0 +1,20 @@
package net.corda.demobench.ui
import com.sun.javafx.scene.control.behavior.TabPaneBehavior
import com.sun.javafx.scene.control.skin.TabPaneSkin
import javafx.scene.Node
import javafx.scene.control.Tab
class CloseableTab(text: String, content: Node) : Tab(text, content) {
fun requestClose() {
val b = behaviour
if (b.canCloseTab(this)) {
b.closeTab(this)
}
}
private val behaviour: TabPaneBehavior
get() = (tabPane.skin as TabPaneSkin).behavior
}

View File

@ -0,0 +1,31 @@
package net.corda.demobench.ui
import javafx.scene.control.Label
import javafx.scene.layout.HBox
class PropertyLabel() : HBox() {
val nameLabel = Label()
val valueLabel = Label()
var name : String
get() = nameLabel.text
set(value) {
nameLabel.text = value
}
var value: String
get() = valueLabel.text
set(value) {
valueLabel.text = value
}
init {
nameLabel.styleClass.add("property-name")
valueLabel.styleClass.add("property-value")
children.addAll(nameLabel, valueLabel)
styleClass.add("property-label")
}
}

View File

@ -0,0 +1,81 @@
package net.corda.demobench.views
import com.jediterm.terminal.TerminalColor
import com.jediterm.terminal.TextStyle
import com.jediterm.terminal.ui.settings.DefaultSettingsProvider
import javafx.application.Platform
import javafx.collections.FXCollections
import javafx.embed.swing.SwingNode
import javafx.scene.Parent
import javafx.scene.control.Button
import javafx.scene.control.TabPane
import javax.swing.SwingUtilities
import net.corda.demobench.pty.R3Pty
import net.corda.demobench.ui.CloseableTab
import tornadofx.*
class DemoBenchView : View("Corda Demo Bench") {
override val root by fxml<Parent>()
val addNodeButton by fxid<Button>()
val nodeTabPane by fxid<TabPane>()
val settingsProvider = TerminalSettingsProvider()
init {
importStylesheet("/net/corda/demobench/style.css")
primaryStage.setOnCloseRequest {
// Close all open tabs
FXCollections.observableArrayList(nodeTabPane.tabs).forEach {
(it as CloseableTab).requestClose()
}
Platform.exit()
}
addNodeButton.setOnAction {
val nodeTab = createNode()
nodeTabPane.selectionModel.select(nodeTab)
}
addNodeButton.fire()
}
fun createNode(): CloseableTab {
val pty = R3Pty("Banksy", settingsProvider, java.awt.Dimension(160, 80))
val nodeTabView = NodeTabView(pty.name)
val nodeTab = CloseableTab(pty.name, nodeTabView.root)
// Ensure that we close the terminal along with the tab.
nodeTab.setOnClosed {
pty.close()
}
val swingTerminal = SwingNode()
SwingUtilities.invokeLater({
swingTerminal.content = pty.terminal
swingTerminal.setOnMouseClicked {
swingTerminal.requestFocus()
}
})
nodeTabPane.tabs.add(nodeTab)
nodeTab.content.add(swingTerminal)
pty.run("/bin/bash", "--login")
return nodeTab
}
class TerminalSettingsProvider : DefaultSettingsProvider() {
override fun getDefaultStyle(): TextStyle {
return TextStyle(TerminalColor.WHITE, TerminalColor.BLACK)
}
override fun emulateX11CopyPaste(): Boolean {
return true
}
}
}

View File

@ -0,0 +1,28 @@
package net.corda.demobench.views
import javafx.scene.control.Button
import javafx.scene.control.Label
import javafx.scene.layout.Priority
import javafx.scene.layout.VBox
import net.corda.demobench.ui.PropertyLabel
import tornadofx.Fragment
import tornadofx.vgrow
class NodeTabView(name: String) : Fragment() {
override val root by fxml<VBox>()
val nodeName by fxid<Label>()
val p2pPort by fxid<PropertyLabel>()
val states by fxid<PropertyLabel>()
val transactions by fxid<PropertyLabel>()
val balance by fxid<PropertyLabel>()
val viewDatabaseButton by fxid<Button>()
val launchExplorerButton by fxid<Button>()
init {
nodeName.text = name
root.vgrow = Priority.ALWAYS
}
}

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator"/>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>demobench.log</file>
<append>false</append>
<encoder>
<pattern>%date %-5level %c{1} - %msg%n</pattern>
</encoder>
</appender>
<root level="info">
<appender-ref ref="FILE" />
</root>
</configuration>

View File

@ -0,0 +1,3 @@
# Register SLF4JBridgeHandler as handler for the j.u.l. root logger
# See http://www.slf4j.org/legacy.html#jul-to-slf4j
handlers = org.slf4j.bridge.SLF4JBridgeHandler

View File

@ -1,72 +0,0 @@
<?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.Tab?>
<?import javafx.scene.control.TabPane?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.Pane?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.layout.VBox?>
<StackPane xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1">
<children>
<TabPane maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" minHeight="444.0" minWidth="800.0" prefHeight="613.0" prefWidth="1231.0" tabClosingPolicy="UNAVAILABLE" tabMinHeight="30.0">
<tabs>
<Tab text="Bank A">
<content>
<VBox prefHeight="953.0" prefWidth="1363.0">
<children>
<HBox prefHeight="95.0" prefWidth="800.0" spacing="15.0" styleClass="header">
<children>
<VBox prefHeight="66.0" prefWidth="296.0" spacing="20.0">
<children>
<Label style="-fx-font-size: 40; -fx-text-fill: red;" text="Bank A" />
<Label style="-fx-text-fill: white;" text="P2P port: 20004" />
</children>
</VBox>
<VBox prefHeight="93.0" prefWidth="267.0">
<children>
<Label maxWidth="1.7976931348623157E308" text="States in vault: 20" />
<Label text="Known transactions: 45" />
<Label text="Balance: $1024" />
</children>
</VBox>
<Pane prefHeight="200.0" prefWidth="200.0" HBox.hgrow="ALWAYS" />
<Button mnemonicParsing="false" prefHeight="92.0" prefWidth="115.0" styleClass="big-button" text="View&#10;Database" textAlignment="CENTER" />
<Button mnemonicParsing="false" prefHeight="92.0" prefWidth="115.0" styleClass="big-button" text="Launch&#10;Explorer" textAlignment="CENTER" />
</children>
</HBox>
<StackPane maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" style="-fx-background-color: black;" VBox.vgrow="ALWAYS">
<children>
<ImageView fitWidth="1000.0" pickOnBounds="true" preserveRatio="true" StackPane.alignment="TOP_LEFT">
<image>
<Image url="@mock-term.png" />
</image>
</ImageView>
</children>
</StackPane>
</children>
</VBox>
</content>
</Tab>
<Tab text="Bank B" />
<Tab text="Bank of Corda">
<content>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0" />
</content>
</Tab>
</tabs>
</TabPane>
<Button mnemonicParsing="false" styleClass="add-node-button" text="Add Node" StackPane.alignment="TOP_RIGHT">
<StackPane.margin>
<Insets right="5.0" top="5.0" />
</StackPane.margin>
</Button>
</children>
</StackPane>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 126 KiB

View File

@ -7,7 +7,7 @@
-fx-padding: 15px; -fx-padding: 15px;
} }
.header Label { .property-label .label {
-fx-font-size: 14pt; -fx-font-size: 14pt;
-fx-text-fill: white; -fx-text-fill: white;
} }

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.TabPane?>
<?import javafx.scene.layout.StackPane?>
<StackPane xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1">
<children>
<TabPane fx:id="nodeTabPane" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" minHeight="444.0" minWidth="800.0" prefHeight="613.0" prefWidth="1231.0" tabClosingPolicy="UNAVAILABLE" tabMinHeight="30.0">
<tabs>
</tabs>
</TabPane>
<Button fx:id="addNodeButton" mnemonicParsing="false" styleClass="add-node-button" text="Add Node" StackPane.alignment="TOP_RIGHT">
<StackPane.margin>
<Insets right="5.0" top="5.0" />
</StackPane.margin>
</Button>
</children>
</StackPane>

View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.Pane?>
<?import javafx.scene.layout.VBox?>
<?import net.corda.demobench.ui.PropertyLabel?>
<VBox prefHeight="953.0" prefWidth="1363.0" xmlns="http://javafx.com/javafx/8.0.121" xmlns:fx="http://javafx.com/fxml/1">
<children>
<HBox prefHeight="95.0" prefWidth="800.0" spacing="15.0" styleClass="header">
<children>
<VBox prefHeight="66.0" prefWidth="296.0" spacing="20.0">
<children>
<Label fx:id="nodeName" style="-fx-font-size: 40; -fx-text-fill: red;" />
<PropertyLabel fx:id="p2pPort" name="P2P port: " />
</children>
</VBox>
<VBox prefHeight="93.0" prefWidth="267.0">
<children>
<PropertyLabel fx:id="states" name="States in vault: " />
<PropertyLabel fx:id="transactions" name="Known transactions: " />
<PropertyLabel fx:id="balance" name="Balance: " />
</children>
</VBox>
<Pane prefHeight="200.0" prefWidth="200.0" HBox.hgrow="ALWAYS" />
<Button fx:id="viewDatabaseButton" mnemonicParsing="false" prefHeight="92.0" prefWidth="115.0" styleClass="big-button" text="View&#10;Database" textAlignment="CENTER" />
<Button fx:id="launchExplorerButton" mnemonicParsing="false" prefHeight="92.0" prefWidth="115.0" styleClass="big-button" text="Launch&#10;Explorer" textAlignment="CENTER" />
</children>
</HBox>
</children>
</VBox>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB