diff --git a/.idea/cssdialects.xml b/.idea/cssdialects.xml new file mode 100644 index 0000000..54c31ad --- /dev/null +++ b/.idea/cssdialects.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/befunge/befunge.iml b/befunge/befunge.iml index da87f9f..9abdb7e 100644 --- a/befunge/befunge.iml +++ b/befunge/befunge.iml @@ -46,5 +46,14 @@ + + + + + + + + + \ No newline at end of file diff --git a/befunge/src/main/kotlin/befide/befunge/state/Vec.kt b/befunge/src/main/kotlin/befide/befunge/state/Vec.kt index 0866d1f..fb6f263 100644 --- a/befunge/src/main/kotlin/befide/befunge/state/Vec.kt +++ b/befunge/src/main/kotlin/befide/befunge/state/Vec.kt @@ -10,4 +10,5 @@ data class Vec(val x: Int, val y: Int) { operator fun times(c: Int) = Vec(x * c, y * c) infix fun mod(other: Vec) = Vec(x mod other.x, y mod other.y) + operator fun unaryMinus() = Vec(-x, -y) } \ No newline at end of file diff --git a/ide/ide.iml b/ide/ide.iml index bf8f97e..7fa9950 100644 --- a/ide/ide.iml +++ b/ide/ide.iml @@ -47,6 +47,21 @@ + + + + + + + + + + + + + + + diff --git a/ide/src/main/kotlin/befide/ide/ActionView.kt b/ide/src/main/kotlin/befide/ide/ActionView.kt index 22a5d99..0ea2060 100644 --- a/ide/src/main/kotlin/befide/ide/ActionView.kt +++ b/ide/src/main/kotlin/befide/ide/ActionView.kt @@ -6,7 +6,7 @@ import javafx.animation.Timeline import javafx.util.Duration import tornadofx.* -class ActionView(val interp: Interpreter, val ioView: IOView) : View() { +class ActionView(val interp: Interpreter, val codeView: CodeView, val ioView: IOView) : View() { var runTimeline: Timeline = timeline(false) { keyframe(Duration.seconds(0.0)) { setOnFinished { @@ -21,25 +21,36 @@ class ActionView(val interp: Interpreter, val ioView: IOView) : View() { override val root = hbox { button("step") { - setOnAction { interp.step() } + setOnAction { + interp.funge.setString(codeView.src) + interp.step() + } } button("reset") { + enableWhen(codeView.lockedProperty) + setOnAction { interp.reset() ioView.reset() + codeView.src = interp.funge.toString() + codeView.locked = false } } button("run") { setOnAction { - runTimeline.rate = 10000000.0 + interp.funge.setString(codeView.src) + codeView.locked = true + runTimeline.rate = 10000.0 runTimeline.playFromStart() } } button("walk") { setOnAction { + interp.funge.setString(codeView.src) + codeView.locked = true runTimeline.rate = 50.0 runTimeline.playFromStart() } @@ -47,6 +58,8 @@ class ActionView(val interp: Interpreter, val ioView: IOView) : View() { button("crawl") { setOnAction { + interp.funge.setString(codeView.src) + codeView.locked = true runTimeline.rate = 4.0 runTimeline.playFromStart() } diff --git a/ide/src/main/kotlin/befide/ide/CodeLabel.kt b/ide/src/main/kotlin/befide/ide/CodeLabel.kt new file mode 100644 index 0000000..5396586 --- /dev/null +++ b/ide/src/main/kotlin/befide/ide/CodeLabel.kt @@ -0,0 +1,37 @@ +package befide.ide + +import befide.befunge.core.Interpreter +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, val interp: Interpreter) : Label() { + val charProperty = SimpleObjectProperty('\u0000') + var char: Char by charProperty + + fun restyle() { + styleClass.setAll("code") + + 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 (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(charProperty.stringBinding { it?.toString() ?: " " }) + + setOnMouseClicked { + cursorPos.value = pos + } + + charProperty.addListener { _, _, _ -> restyle() } + + char = ' ' + } +} \ No newline at end of file diff --git a/ide/src/main/kotlin/befide/ide/CodeView.kt b/ide/src/main/kotlin/befide/ide/CodeView.kt index 185b083..2fdf773 100644 --- a/ide/src/main/kotlin/befide/ide/CodeView.kt +++ b/ide/src/main/kotlin/befide/ide/CodeView.kt @@ -1,20 +1,131 @@ package befide.ide import befide.befunge.core.Interpreter -import javafx.beans.property.SimpleStringProperty +import befide.befunge.state.Vec +import javafx.beans.property.ObjectProperty +import javafx.beans.property.SimpleBooleanProperty +import javafx.beans.property.SimpleObjectProperty +import javafx.scene.input.KeyCode import tornadofx.* +import tornadofx.getValue +import tornadofx.setValue + +operator fun List>.get(v: Vec): T = this[v.y][v.x] class CodeView(val interp: Interpreter) : View() { - val srcProperty = SimpleStringProperty("") - var src by srcProperty + val cursorPosProperty: ObjectProperty = SimpleObjectProperty(Vec(0, 0)) + var cursorPos by cursorPosProperty + + val cursorDeltaProperty = SimpleObjectProperty(Vec(1, 0)) + var cursorDelta by cursorDeltaProperty + + val lockedProperty = SimpleBooleanProperty(false) + var locked by lockedProperty + + var labels: List> = List(25) { y -> List(80) { x -> CodeLabel(Vec(x, y), cursorPosProperty, interp) } } init { - interp.fungeChanged += { src = interp.funge.toString() } - srcProperty.addListener { _, _, newValue -> interp.funge.setString(newValue) } + 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].char = change.to.asChar ?: '\u2022' + labels[change.vec].restyle() + } + } } - override val root = textarea(srcProperty) { - prefRowCount = 25 - prefColumnCount = 80 + fun move(delta: Vec? = null) { + cursorPos = interp.funge.nextVec(cursorPos, delta ?: cursorDelta) + } + + var src: String + get() = labels.joinToString("\n") { row -> + row.dropLastWhile { lbl -> + lbl.char.isWhitespace() + }.joinToString("") { lbl -> + lbl.char.toString() + } + } + set(value) { + val lines = value.lines() + + for (row in labels) { + for (lbl in row) { + lbl.char = lines.getOrNull(lbl.pos.y)?.getOrNull(lbl.pos.x) ?: ' ' + } + } + } + + 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 { + if (locked) return@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].char = ch + + move() + } + + ch == '\u0008' -> { // backspace + move(-cursorDelta) + + labels[cursorPos].char = ' ' + } + + else -> { + println("'$ch' (${ch.toInt()})") + } + } + } + } } } \ No newline at end of file diff --git a/ide/src/main/kotlin/befide/ide/EditorView.kt b/ide/src/main/kotlin/befide/ide/EditorView.kt index 2403271..567a216 100644 --- a/ide/src/main/kotlin/befide/ide/EditorView.kt +++ b/ide/src/main/kotlin/befide/ide/EditorView.kt @@ -10,18 +10,14 @@ class EditorView : View("Befide") { private val codeView = CodeView(interp) private val stackView = StackView(interp) private val ioView = IOView(interp) - private val actionView = ActionView(interp, ioView) + private val actionView = ActionView(interp, codeView, ioView) override val root = borderpane { top { add(actionView) } - center { - add(codeView) - } + center { add(codeView) } - right { - add(stackView) - } + right { add(stackView) } bottom { add(ioView) } } @@ -31,31 +27,13 @@ class EditorView : View("Befide") { ^ 0 v +1\ _^#-+*< >22g02g*"_@"*-!1- #v_v> >:>::3g: ,\188 ^^ -1\g21\g22