working framework
This commit is contained in:
@@ -1,185 +0,0 @@
|
||||
package befide.ide
|
||||
|
||||
import befide.befunge.core.Interpreter
|
||||
import befide.befunge.state.IpMode
|
||||
import javafx.animation.Animation
|
||||
import javafx.animation.Timeline
|
||||
import javafx.beans.property.SimpleBooleanProperty
|
||||
import javafx.beans.property.SimpleObjectProperty
|
||||
import javafx.stage.FileChooser
|
||||
import javafx.util.Duration
|
||||
import tornadofx.*
|
||||
import tornadofx.getValue
|
||||
import tornadofx.setValue
|
||||
import java.io.File
|
||||
|
||||
class ActionView(val interp: Interpreter, val codeView: CodeView, val ioView: IOView, val editorView: EditorView) : View() {
|
||||
val stepProperty = SimpleBooleanProperty(false)
|
||||
var step by stepProperty
|
||||
|
||||
var runTimeline: Timeline = timeline(false) {
|
||||
keyframe(Duration.seconds(0.0)) {
|
||||
setOnFinished {
|
||||
step = interp.step()
|
||||
}
|
||||
}
|
||||
keyframe(Duration.seconds(1.0)) {}
|
||||
|
||||
cycleCount = Animation.INDEFINITE
|
||||
}
|
||||
|
||||
val isRunningProperty = SimpleBooleanProperty(false)
|
||||
var isRunning by isRunningProperty
|
||||
|
||||
val canResetProperty = SimpleBooleanProperty(false)
|
||||
var canReset by canResetProperty
|
||||
|
||||
val saveFileProperty = SimpleObjectProperty<File>()
|
||||
var saveFile by saveFileProperty
|
||||
|
||||
fun start(rate: Double) {
|
||||
stop()
|
||||
|
||||
if (interp.ip.mode == IpMode.Inactive) reset()
|
||||
|
||||
isRunning = true
|
||||
canReset = true
|
||||
|
||||
interp.funge.values = codeView.values
|
||||
|
||||
runTimeline.rate = rate
|
||||
runTimeline.playFromStart()
|
||||
}
|
||||
|
||||
fun step() {
|
||||
canReset = true
|
||||
|
||||
interp.funge.values = codeView.values
|
||||
|
||||
step = interp.step()
|
||||
}
|
||||
|
||||
fun stop() {
|
||||
isRunning = false
|
||||
|
||||
runTimeline.stop()
|
||||
}
|
||||
|
||||
fun reset() {
|
||||
stop()
|
||||
interp.reset()
|
||||
ioView.reset()
|
||||
canReset = false
|
||||
}
|
||||
|
||||
fun clearCode() {
|
||||
reset()
|
||||
codeView.clear()
|
||||
}
|
||||
|
||||
fun save() {
|
||||
if (saveFile == null) {
|
||||
saveAs()
|
||||
} else {
|
||||
saveFile.writeText(codeView.src)
|
||||
}
|
||||
}
|
||||
|
||||
private val chooser: FileChooser = FileChooser().apply {
|
||||
title = "Befunge File"
|
||||
extensionFilters.setAll(
|
||||
FileChooser.ExtensionFilter("Befunge 93", "*.bf", "*.b93"),
|
||||
FileChooser.ExtensionFilter("Befunge 98", "*.bf", "*.b98"))
|
||||
}
|
||||
|
||||
fun saveAs() {
|
||||
val file: File? = chooser.showSaveDialog(primaryStage)
|
||||
if (file != null) {
|
||||
saveFile = file
|
||||
save()
|
||||
}
|
||||
}
|
||||
|
||||
fun open() {
|
||||
val file: File? = chooser.showOpenDialog(primaryStage)
|
||||
if (file != null) {
|
||||
saveFile = file
|
||||
|
||||
editorView.title = "${saveFile.nameWithoutExtension} [${saveFile.absolutePath}] - Befide"
|
||||
|
||||
reset()
|
||||
codeView.src = saveFile.readText()
|
||||
}
|
||||
}
|
||||
|
||||
fun new() {
|
||||
clearCode()
|
||||
saveFile = null
|
||||
editorView.title = "Befide"
|
||||
}
|
||||
|
||||
override val root = hbox {
|
||||
button("step") {
|
||||
setOnAction { step() }
|
||||
disableWhen(isRunningProperty)
|
||||
}
|
||||
|
||||
separator {}
|
||||
|
||||
button("run") {
|
||||
setOnAction { start(10000.0) }
|
||||
}
|
||||
|
||||
button("walk") {
|
||||
setOnAction { start(50.0) }
|
||||
}
|
||||
|
||||
button("crawl") {
|
||||
setOnAction { start(4.0) }
|
||||
}
|
||||
|
||||
separator {}
|
||||
|
||||
button("stop") {
|
||||
setOnAction { stop() }
|
||||
enableWhen(isRunningProperty)
|
||||
}
|
||||
|
||||
spacer {}
|
||||
|
||||
button("reset") {
|
||||
setOnAction { reset() }
|
||||
enableWhen(canResetProperty)
|
||||
}
|
||||
|
||||
separator {}
|
||||
|
||||
button("open") {
|
||||
setOnAction { open() }
|
||||
disableWhen(isRunningProperty)
|
||||
}
|
||||
|
||||
button("save as") {
|
||||
setOnAction { saveAs() }
|
||||
}
|
||||
|
||||
button("save") {
|
||||
setOnAction { save() }
|
||||
|
||||
enableWhen(saveFileProperty.isNotNull.and(isRunningProperty.not()))
|
||||
}
|
||||
|
||||
button("new") {
|
||||
setOnAction { new() }
|
||||
disableWhen(isRunningProperty)
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
stepProperty.onChange {
|
||||
if (!it) stop()
|
||||
}
|
||||
|
||||
new()
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
package befide.ide
|
||||
|
||||
import befide.befunge.core.Interpreter
|
||||
import befide.befunge.state.Value
|
||||
import befide.befunge.state.Vec
|
||||
import javafx.beans.property.ObjectProperty
|
||||
import javafx.beans.property.SimpleObjectProperty
|
||||
import javafx.scene.control.Label
|
||||
import tornadofx.*
|
||||
|
||||
class CodeLabel(val pos: Vec, val cursorPos: ObjectProperty<Vec>, val interp: Interpreter) : Label() {
|
||||
var valueProperty = SimpleObjectProperty<Value>(Value(' '))
|
||||
var value: Value by valueProperty
|
||||
|
||||
fun restyle() {
|
||||
styleClass.setAll("code")
|
||||
|
||||
val char = value.asChar ?: '\u2022'
|
||||
|
||||
if (char in "0123456789") styleClass.add("code-num")
|
||||
if (char in "gp") styleClass.add("code-funge")
|
||||
if (char in "<>^v?#") styleClass.add("code-dir")
|
||||
if (char in "@") styleClass.add("code-stop")
|
||||
if (char in "_|") styleClass.add("code-condition")
|
||||
if (char in "\"") styleClass.add("code-quote")
|
||||
|
||||
if (pos == cursorPos.value) styleClass.add("code-cursor")
|
||||
if (pos == interp.ip.pos) styleClass.add("code-cursor-ip")
|
||||
if (char == '\u2022') styleClass.add("unknown")
|
||||
}
|
||||
|
||||
init {
|
||||
textProperty().bind(valueProperty.stringBinding { it?.asChar?.toString() ?: "\u2022" })
|
||||
|
||||
setOnMouseClicked {
|
||||
cursorPos.value = pos
|
||||
}
|
||||
|
||||
valueProperty.addListener { _, _, _ -> restyle() }
|
||||
|
||||
restyle()
|
||||
}
|
||||
}
|
||||
@@ -1,160 +0,0 @@
|
||||
package befide.ide
|
||||
|
||||
import befide.befunge.core.Interpreter
|
||||
import befide.befunge.state.Value
|
||||
import befide.befunge.state.Vec
|
||||
import javafx.beans.property.ObjectProperty
|
||||
import javafx.beans.property.SimpleObjectProperty
|
||||
import javafx.scene.input.KeyCode
|
||||
import tornadofx.*
|
||||
|
||||
operator fun <T> List<List<T>>.get(v: Vec): T = this[v.y][v.x]
|
||||
|
||||
class CodeView(val interp: Interpreter) : View() {
|
||||
val cursorPosProperty: ObjectProperty<Vec> = SimpleObjectProperty<Vec>(Vec(0, 0))
|
||||
var cursorPos by cursorPosProperty
|
||||
|
||||
val cursorDeltaProperty = SimpleObjectProperty<Vec>(Vec(1, 0))
|
||||
var cursorDelta by cursorDeltaProperty
|
||||
|
||||
var labels: List<List<CodeLabel>> = List(25) { y -> List(80) { x -> CodeLabel(Vec(x, y), cursorPosProperty, interp) } }
|
||||
|
||||
var values: List<List<Value>>
|
||||
get() = labels.map { it.map { it.value } }
|
||||
set(data) {
|
||||
for (y in 0 until labels.size)
|
||||
for (x in 0 until labels[y].size)
|
||||
labels[y][x].value = data[y][x]
|
||||
}
|
||||
|
||||
init {
|
||||
cursorPosProperty.addListener { _, old, new ->
|
||||
labels[old].restyle()
|
||||
labels[new].restyle()
|
||||
}
|
||||
|
||||
interp.ipChanged += {
|
||||
labels[it.from.pos].restyle()
|
||||
labels[it.to.pos].restyle()
|
||||
}
|
||||
|
||||
interp.fungeChanged += {
|
||||
for (change in it.changes) {
|
||||
labels[change.vec].value = change.to
|
||||
labels[change.vec].restyle()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun move(delta: Vec? = null) {
|
||||
cursorPos = interp.funge.nextVec(cursorPos, delta ?: cursorDelta)
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
for (row in labels) {
|
||||
for (lbl in row) {
|
||||
lbl.value = Value(' ')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var src: String
|
||||
get() = labels.joinToString("\n") { row ->
|
||||
row.dropLastWhile { lbl ->
|
||||
lbl.value.asChar?.isWhitespace() ?: false
|
||||
}.joinToString("") { lbl ->
|
||||
lbl.value.asChar?.toString() ?: "\u2022"
|
||||
}
|
||||
}
|
||||
set(value) {
|
||||
val lines = value.lines()
|
||||
|
||||
for (row in labels) {
|
||||
for (lbl in row) {
|
||||
val char = lines.getOrNull(lbl.pos.y)?.getOrNull(lbl.pos.x) ?: ' '
|
||||
lbl.value = Value(char)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override val root = hbox {
|
||||
isFocusTraversable = true
|
||||
|
||||
addClass("code-view")
|
||||
|
||||
vbox {
|
||||
children.setAll(labels.map { row -> hbox { children.setAll(row) } })
|
||||
}
|
||||
|
||||
setOnMouseClicked {
|
||||
requestFocus()
|
||||
}
|
||||
|
||||
setOnKeyPressed {
|
||||
when (it.code) {
|
||||
KeyCode.RIGHT -> move(Vec(1, 0))
|
||||
KeyCode.LEFT -> move(Vec(-1, 0))
|
||||
KeyCode.DOWN -> move(Vec(0, 1))
|
||||
KeyCode.UP -> move(Vec(0, -1))
|
||||
else -> return@setOnKeyPressed
|
||||
}
|
||||
if (it.isAltDown) when (it.code) {
|
||||
KeyCode.RIGHT -> cursorDelta = Vec(1, 0)
|
||||
KeyCode.LEFT -> cursorDelta = Vec(-1, 0)
|
||||
KeyCode.DOWN -> cursorDelta = Vec(0, 1)
|
||||
KeyCode.UP -> cursorDelta = Vec(0, -1)
|
||||
else -> Unit
|
||||
}
|
||||
it.consume()
|
||||
}
|
||||
|
||||
setOnKeyTyped {
|
||||
for (ch in it.character) {
|
||||
when {
|
||||
!ch.isISOControl() -> {
|
||||
cursorDelta = when (ch) {
|
||||
'>' -> Vec(1, 0)
|
||||
'<' -> Vec(-1, 0)
|
||||
'v' -> Vec(0, 1)
|
||||
'^' -> Vec(0, -1)
|
||||
else -> cursorDelta
|
||||
}
|
||||
|
||||
labels[cursorPos].value = Value(ch)
|
||||
|
||||
move()
|
||||
}
|
||||
|
||||
ch == '\u0008' -> { // backspace
|
||||
move(-cursorDelta)
|
||||
|
||||
labels[cursorPos].value = Value(' ')
|
||||
}
|
||||
|
||||
ch == '\u000d' -> when (cursorDelta) { // return
|
||||
Vec(1, 0) -> {
|
||||
move(Vec(0, 1))
|
||||
cursorPos = Vec(0, cursorPos.y)
|
||||
}
|
||||
Vec(-1, 0) -> {
|
||||
move(Vec(0, -1))
|
||||
cursorPos = Vec(interp.funge.width - 1, cursorPos.y)
|
||||
}
|
||||
Vec(0, 1) -> {
|
||||
move(Vec(-1, 0))
|
||||
cursorPos = Vec(cursorPos.x, 0)
|
||||
}
|
||||
Vec(0, -1) -> {
|
||||
move(Vec(1, 0))
|
||||
cursorPos = Vec(cursorPos.x, interp.funge.height - 1)
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
println("'$ch' (${ch.toInt()})")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,34 +1,12 @@
|
||||
package befide.ide
|
||||
|
||||
import befide.befunge.b93.B93Interpreter
|
||||
import befide.befunge.core.Interpreter
|
||||
import tornadofx.*
|
||||
|
||||
class EditorView : View("Befide") {
|
||||
private var interp: Interpreter = B93Interpreter()
|
||||
|
||||
private val codeView = CodeView(interp)
|
||||
private val stackView = StackView(interp)
|
||||
private val ioView = IOView(interp)
|
||||
private val actionView = ActionView(interp, codeView, ioView, this)
|
||||
|
||||
override val root = borderpane {
|
||||
top { add(actionView) }
|
||||
|
||||
center { add(codeView) }
|
||||
|
||||
right { add(stackView) }
|
||||
|
||||
bottom { add(ioView) }
|
||||
}
|
||||
|
||||
init {
|
||||
primaryStage.isResizable = false
|
||||
}
|
||||
|
||||
override fun onDock() {
|
||||
super.onDock()
|
||||
|
||||
codeView.root.requestFocus()
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
package befide.ide
|
||||
|
||||
import befide.befunge.core.Interpreter
|
||||
import javafx.beans.property.SimpleStringProperty
|
||||
import javafx.event.EventHandler
|
||||
import tornadofx.*
|
||||
import tornadofx.getValue
|
||||
import tornadofx.setValue
|
||||
|
||||
class IOView(val interp: Interpreter) : View() {
|
||||
val outputProperty = SimpleStringProperty("")
|
||||
var output by outputProperty
|
||||
val oldinputProperty = SimpleStringProperty("")
|
||||
var oldinput by oldinputProperty
|
||||
|
||||
|
||||
init {
|
||||
interp.outputChanged += {
|
||||
var str = ""
|
||||
while (!interp.stdOutput.isEmpty()) {
|
||||
str += interp.stdOutput.remove()
|
||||
}
|
||||
output += str
|
||||
}
|
||||
// add listeners to interp, handle streams, idk
|
||||
}
|
||||
|
||||
override val root = vbox {
|
||||
addClass("")
|
||||
textarea(outputProperty) {
|
||||
isWrapText = true
|
||||
isEditable = false
|
||||
// width, height, idk
|
||||
prefHeight = 200.0
|
||||
}
|
||||
textarea(oldinputProperty) {
|
||||
isEditable = false
|
||||
prefHeight = 0.0
|
||||
|
||||
oldinputProperty.onChange { scrollTop = Double.MAX_VALUE }
|
||||
}
|
||||
textfield {
|
||||
onAction = EventHandler {
|
||||
oldinput += this.text + '\n'
|
||||
this.text.forEach { c -> interp.stdInput.add(c) }
|
||||
this.clear()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun reset() {
|
||||
output = ""
|
||||
oldinput = ""
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
package befide.ide
|
||||
|
||||
import befide.befunge.b93.padEnd
|
||||
import befide.befunge.core.Interpreter
|
||||
import befide.befunge.state.Value
|
||||
import javafx.beans.property.SimpleListProperty
|
||||
import javafx.beans.property.SimpleObjectProperty
|
||||
import javafx.beans.property.SimpleStringProperty
|
||||
import javafx.collections.FXCollections
|
||||
import javafx.scene.layout.Priority
|
||||
import javafx.scene.text.Font
|
||||
import tornadofx.*
|
||||
import tornadofx.getValue
|
||||
import tornadofx.setValue
|
||||
|
||||
class StackView(val interp: Interpreter) : View() {
|
||||
val charOutProperty = SimpleStringProperty()
|
||||
var charOut by charOutProperty
|
||||
|
||||
override val root = textarea(charOutProperty) {
|
||||
addClass("stack-pane")
|
||||
prefWidth = 150.0
|
||||
isEditable = false
|
||||
}
|
||||
|
||||
private fun <T> getStackStr(mapping: (Value) -> T): String {
|
||||
val num = interp.funge.height - 3
|
||||
return interp.stack.takeLast(num)
|
||||
.map(mapping)
|
||||
.padEnd(num, "")
|
||||
// .reversed()
|
||||
.joinToString("\n")
|
||||
}
|
||||
|
||||
init {
|
||||
charOut = getStackStr { "${it.asChar ?: '\u2022'} (${it.value})" }
|
||||
|
||||
interp.stackChanged += {
|
||||
charOut = getStackStr { "${it.asChar ?: '\u2022'} (${it.value})" }
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user