Compare commits
41 Commits
621dfb71ca
...
b0be0ba8b4
| Author | SHA1 | Date | |
|---|---|---|---|
| b0be0ba8b4 | |||
| a4e4e103bd | |||
| 3e7d993662 | |||
| dabc42ea25 | |||
| af8241505f | |||
| d56b871554 | |||
| 8934d40c0b | |||
| 8f7739c26a | |||
| a1ce5957d1 | |||
| 9e128eaad2 | |||
| 881aad743d | |||
| df86bae738 | |||
| c731dcef36 | |||
| e14138c7a0 | |||
| d684360547 | |||
| a9eb569a02 | |||
| 77d330a71d | |||
| 391c6b19fd | |||
| 632173d122 | |||
| 7aa1306077 | |||
| 15b7112348 | |||
| 2cbcc86dce | |||
| f71505ce28 | |||
| cb1f892e6d | |||
| b54a25eec6 | |||
| a87f50eec7 | |||
| 95a9ab63d0 | |||
| f6c0feda4b | |||
| 916831743e | |||
| 72e71c8e36 | |||
| 8180599c04 | |||
| 0b965d32b6 | |||
| f988c79e2f | |||
| 45a8851e2a | |||
| a6ed7818da | |||
| 3cb1811dcb | |||
| c6e5a836ea | |||
| a7dbdff535 | |||
| 2b8aa06117 | |||
| 1863462b44 | |||
| 209e40d0d5 |
2
Makefile
2
Makefile
@@ -14,7 +14,7 @@ all: clean build
|
|||||||
|
|
||||||
build: $(MIN_PATH)
|
build: $(MIN_PATH)
|
||||||
|
|
||||||
debug: HAXE_FLAGS += -D webconsole --debug
|
debug: HAXE_FLAGS += -D webconsole -D error_stack --debug
|
||||||
debug: build
|
debug: build
|
||||||
|
|
||||||
$(HAXE_PATH): $(shell find src -name '*.hx')
|
$(HAXE_PATH): $(shell find src -name '*.hx')
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ General purpose "operation system" for [ComputerCraft](https://tweaked.cc/) buil
|
|||||||
- Hardware abstraction
|
- Hardware abstraction
|
||||||
- Virtual screens to switch between multiple GUI apps
|
- Virtual screens to switch between multiple GUI apps
|
||||||
- Reactive UI framework
|
- Reactive UI framework
|
||||||
|
- Solid base to easily applications
|
||||||
|
|
||||||
# Building
|
# Building
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
-p src
|
-p src
|
||||||
--main Startup
|
--main kernel.Entrypoint
|
||||||
|
|
||||||
--library cctweaked:git:https://git.kapelle.org/niklas/cctweaked-haxelib.git
|
--library cctweaked:git:https://git.kapelle.org/niklas/cctweaked-haxelib.git
|
||||||
--library tink_core
|
--library tink_core
|
||||||
|
|||||||
@@ -1,78 +1,10 @@
|
|||||||
import kernel.http.HTTPRequest.Http;
|
import lib.HomeContext;
|
||||||
import util.Observable;
|
|
||||||
import lib.ui.reactive.TextElement;
|
|
||||||
import lib.ui.reactive.ReactiveUI;
|
|
||||||
import kernel.ui.WindowManager;
|
|
||||||
import kernel.net.Net;
|
|
||||||
import kernel.KernelEvents;
|
|
||||||
import kernel.Log;
|
|
||||||
import kernel.Init;
|
|
||||||
|
|
||||||
using util.Extender.LambdaExtender;
|
|
||||||
|
|
||||||
class Startup {
|
class Startup {
|
||||||
public static function main() {
|
public static function main() {
|
||||||
Init.initKernel();
|
var main = new HomeContext();
|
||||||
httpTest();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function httpTest() {
|
main.run();
|
||||||
Log.moveToOutput("main");
|
|
||||||
// var url = "https://mock.codes/400";
|
|
||||||
// var url = "https://jsonplaceholder.typicode.com/todos/1";
|
|
||||||
var url = "https://domainnotexsist.net/aaaa";
|
|
||||||
Http.request(url).handle((outcome)->{
|
|
||||||
switch outcome{
|
|
||||||
case Success(data):
|
|
||||||
Log.debug(data.body);
|
|
||||||
case Failure(failure):
|
|
||||||
Log.error(failure.reason);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function uiTest() {
|
|
||||||
var context = WindowManager.instance.createNewContext();
|
|
||||||
|
|
||||||
var text1 = new Observable("My text");
|
|
||||||
var text2 = new Observable("Another one");
|
|
||||||
|
|
||||||
var ui = new ReactiveUI(context,[
|
|
||||||
new TextElement(text1,Orange,White,{
|
|
||||||
onClick: (p) ->{
|
|
||||||
text1.set("Click");
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
new TextElement(text2,Black,White,{
|
|
||||||
onClick: (p)->{
|
|
||||||
text2.set("Click");
|
|
||||||
}
|
|
||||||
})
|
|
||||||
]);
|
|
||||||
|
|
||||||
ui.render();
|
|
||||||
WindowManager.instance.focusContextToOutput(context,"main");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function sendTest() {
|
|
||||||
Net.instance.registerProto("ping",(pack)->{
|
|
||||||
Log.debug("Message: " + pack.data);
|
|
||||||
pack.respond("Hello from: "+Net.instance.networkID);
|
|
||||||
});
|
|
||||||
|
|
||||||
KernelEvents.instance.onChar.handle((char)->{
|
|
||||||
if (char == "s"){
|
|
||||||
Log.debug("Sending to 4");
|
|
||||||
Net.instance.sendAndAwait(4,"ping","Hello world").handle((result)->{
|
|
||||||
switch (result){
|
|
||||||
case Success(pack):
|
|
||||||
Log.debug("Response: "+pack.data);
|
|
||||||
case Failure(err):
|
|
||||||
Log.error(err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
137
src/bin/Disk.hx
Normal file
137
src/bin/Disk.hx
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
package bin;
|
||||||
|
|
||||||
|
import kernel.peripherals.Peripherals.Peripheral;
|
||||||
|
import lib.cli.TermHandle;
|
||||||
|
import lib.cli.CLIApp;
|
||||||
|
|
||||||
|
using tink.CoreApi;
|
||||||
|
using Lambda;
|
||||||
|
|
||||||
|
class Disk extends CLIApp {
|
||||||
|
|
||||||
|
private var handle:TermHandle;
|
||||||
|
|
||||||
|
public function new() {}
|
||||||
|
|
||||||
|
public function invoke(handle:TermHandle):Future<Bool> {
|
||||||
|
this.handle = handle;
|
||||||
|
var subcommand = handle.args[0];
|
||||||
|
var driveAddr:Null<String> = handle.args[1];
|
||||||
|
|
||||||
|
switch (subcommand) {
|
||||||
|
case "ls":
|
||||||
|
Peripheral.instance.getDrives().foreach(drive -> {
|
||||||
|
var addr = drive.getAddr();
|
||||||
|
var label = drive.getDiskLabel();
|
||||||
|
var id = drive.getDiskID();
|
||||||
|
|
||||||
|
if (drive.isDiskPresent()){
|
||||||
|
if (drive.hasAudio()){
|
||||||
|
handle.writeLn('${addr} => ${label} [AUDIO]');
|
||||||
|
}else{
|
||||||
|
handle.writeLn('${addr} => ${label} (${id})');
|
||||||
|
}
|
||||||
|
}else {
|
||||||
|
handle.writeLn('${addr} => [NO DISK]');
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
case "play":
|
||||||
|
var drive = Peripheral.instance.getDrive(driveAddr);
|
||||||
|
|
||||||
|
if (drive == null){
|
||||||
|
handle.writeLn("Drive not found: " + driveAddr);
|
||||||
|
return Future.sync(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!drive.isDiskPresent()){
|
||||||
|
handle.writeLn("No disk in drive: " + driveAddr);
|
||||||
|
return Future.sync(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!drive.hasAudio()){
|
||||||
|
handle.writeLn("Disk in drive " + driveAddr + " does not have audio");
|
||||||
|
return Future.sync(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
drive.playAudio();
|
||||||
|
case "stop":
|
||||||
|
var drive = Peripheral.instance.getDrive(driveAddr);
|
||||||
|
|
||||||
|
if (drive == null){
|
||||||
|
handle.writeLn("Drive not found: " + driveAddr);
|
||||||
|
return Future.sync(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!drive.isDiskPresent()){
|
||||||
|
handle.writeLn("No disk in drive: " + driveAddr);
|
||||||
|
return Future.sync(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!drive.hasAudio()){
|
||||||
|
handle.writeLn("Disk in drive: " + driveAddr + " does not have audio");
|
||||||
|
return Future.sync(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
drive.stopAudio();
|
||||||
|
|
||||||
|
case "eject":
|
||||||
|
var drive = Peripheral.instance.getDrive(driveAddr);
|
||||||
|
|
||||||
|
if (drive == null){
|
||||||
|
handle.writeLn("Drive not found: " + driveAddr);
|
||||||
|
return Future.sync(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!drive.isDiskPresent()){
|
||||||
|
handle.writeLn("No disk in drive: " + driveAddr);
|
||||||
|
return Future.sync(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
drive.ejectDisk();
|
||||||
|
case "lable":
|
||||||
|
var drive = Peripheral.instance.getDrive(driveAddr);
|
||||||
|
var label:String = handle.args[2];
|
||||||
|
|
||||||
|
if (drive == null){
|
||||||
|
handle.writeLn("Drive not found: " + driveAddr);
|
||||||
|
return Future.sync(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!drive.isDiskPresent()){
|
||||||
|
handle.writeLn("No disk in drive: " + driveAddr);
|
||||||
|
return Future.sync(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (label == null || label == ""){
|
||||||
|
handle.writeLn(drive.getDiskLabel());
|
||||||
|
}else{
|
||||||
|
var err = drive.setDiskLabel(label);
|
||||||
|
if (err != null){
|
||||||
|
handle.writeLn("Failed to set lable");
|
||||||
|
return Future.sync(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "help":
|
||||||
|
case null:
|
||||||
|
printHelp();
|
||||||
|
default:
|
||||||
|
handle.writeLn("Unknown subcommand: " + subcommand);
|
||||||
|
printHelp();
|
||||||
|
return Future.sync(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Future.sync(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function printHelp() {
|
||||||
|
handle.writeLn("Usage: disk <subcommand> [args]");
|
||||||
|
handle.writeLn("Subcommands:");
|
||||||
|
handle.writeLn(" ls");
|
||||||
|
handle.writeLn(" play <drive>");
|
||||||
|
handle.writeLn(" stop <drive>");
|
||||||
|
handle.writeLn(" eject <drive>");
|
||||||
|
handle.writeLn(" label <drive> [label]");
|
||||||
|
}
|
||||||
|
}
|
||||||
21
src/bin/HelloWorld.hx
Normal file
21
src/bin/HelloWorld.hx
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
package bin;
|
||||||
|
|
||||||
|
import lib.cli.TermHandle;
|
||||||
|
import lib.cli.CLIApp;
|
||||||
|
|
||||||
|
using tink.CoreApi;
|
||||||
|
|
||||||
|
class HelloWorld extends CLIApp {
|
||||||
|
|
||||||
|
public function new() {}
|
||||||
|
|
||||||
|
public function invoke(handle: TermHandle):Future<Bool> {
|
||||||
|
var world:String = "world";
|
||||||
|
if (handle.args.length > 0) {
|
||||||
|
world = handle.args[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
handle.write('Hello, $world!');
|
||||||
|
return Future.sync(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
106
src/bin/Net.hx
Normal file
106
src/bin/Net.hx
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
package bin;
|
||||||
|
|
||||||
|
import kernel.peripherals.Peripherals.Peripheral;
|
||||||
|
import kernel.net.Routing;
|
||||||
|
import haxe.ds.ReadOnlyArray;
|
||||||
|
import lib.cli.TermHandle;
|
||||||
|
import lib.cli.CLIApp;
|
||||||
|
|
||||||
|
using tink.CoreApi;
|
||||||
|
|
||||||
|
class Net extends CLIApp {
|
||||||
|
private var handle:TermHandle;
|
||||||
|
|
||||||
|
public function new() {}
|
||||||
|
|
||||||
|
public function invoke(handle:TermHandle):Future<Bool> {
|
||||||
|
this.handle = handle;
|
||||||
|
|
||||||
|
var subcommand = handle.args[0];
|
||||||
|
var subcommand_args = handle.args.slice(1);
|
||||||
|
|
||||||
|
switch (subcommand) {
|
||||||
|
case "route":
|
||||||
|
return Future.sync(route(subcommand_args));
|
||||||
|
case "iface":
|
||||||
|
return Future.sync(iface(subcommand_args));
|
||||||
|
case "help":
|
||||||
|
printHelp();
|
||||||
|
return Future.sync(true);
|
||||||
|
case "ping":
|
||||||
|
return ping(subcommand_args);
|
||||||
|
case "proto":
|
||||||
|
return Future.sync(protos());
|
||||||
|
default:
|
||||||
|
handle.writeLn("Unknown subcommand: " + subcommand);
|
||||||
|
printHelp();
|
||||||
|
return Future.sync(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function printHelp() {
|
||||||
|
handle.writeLn("net route");
|
||||||
|
handle.writeLn("net iface");
|
||||||
|
handle.writeLn("net help");
|
||||||
|
handle.writeLn("net proto");
|
||||||
|
}
|
||||||
|
|
||||||
|
private function route(args:ReadOnlyArray<String>):Bool {
|
||||||
|
var routes = Routing.instance.getRouteTable();
|
||||||
|
|
||||||
|
for(k => v in routes) {
|
||||||
|
handle.writeLn('${k} => ${v.interf.name()}(${v.cost})');
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function iface(args:ReadOnlyArray<String>):Bool {
|
||||||
|
var modems = Peripheral.instance.getModems();
|
||||||
|
|
||||||
|
for (modem in modems) {
|
||||||
|
handle.writeLn(modem.name());
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ping(args:ReadOnlyArray<String>): Future<Bool> {
|
||||||
|
return new Future<Bool>(trigger -> {
|
||||||
|
if (args.length != 1) {
|
||||||
|
handle.writeLn("Usage: net ping id");
|
||||||
|
trigger(false);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var toID:Null<Int> = Std.parseInt(args[0]);
|
||||||
|
|
||||||
|
if (toID == null) {
|
||||||
|
handle.writeLn("Invalid ID");
|
||||||
|
trigger(false);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
kernel.net.Net.instance.ping(toID).handle(result -> {
|
||||||
|
switch (result){
|
||||||
|
case Success(_):
|
||||||
|
handle.writeLn("Ping succeeded");
|
||||||
|
trigger(true);
|
||||||
|
case Failure(failure):
|
||||||
|
handle.writeLn("Ping failed: " + failure);
|
||||||
|
trigger(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function protos():Bool {
|
||||||
|
var protos = kernel.net.Net.instance.getActiveProtocols();
|
||||||
|
|
||||||
|
for (proto in protos) {
|
||||||
|
handle.writeLn(proto);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
44
src/bin/Redstone.hx
Normal file
44
src/bin/Redstone.hx
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
package bin;
|
||||||
|
|
||||||
|
import kernel.peripherals.Peripherals.Peripheral;
|
||||||
|
import kernel.peripherals.Side;
|
||||||
|
import lib.cli.TermHandle;
|
||||||
|
import lib.cli.CLIApp;
|
||||||
|
|
||||||
|
using tink.CoreApi;
|
||||||
|
|
||||||
|
class Redstone extends CLIApp {
|
||||||
|
public function new() {}
|
||||||
|
|
||||||
|
public function invoke(handle:TermHandle):Future<Bool> {
|
||||||
|
var subcommand = handle.args[0];
|
||||||
|
|
||||||
|
if (subcommand == null) {
|
||||||
|
handle.writeLn("Usage: redstone <on|off|get> <side>");
|
||||||
|
return Future.sync(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
var side:Null<Side> = handle.args[1];
|
||||||
|
if (side == null) {
|
||||||
|
handle.writeLn("Invalid side");
|
||||||
|
return Future.sync(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (subcommand) {
|
||||||
|
case "on":
|
||||||
|
Peripheral.instance.getRedstone(side).setOutput(true);
|
||||||
|
case "off":
|
||||||
|
Peripheral.instance.getRedstone(side).setOutput(false);
|
||||||
|
case "get":
|
||||||
|
var value = Peripheral.instance.getRedstone(side).getAnalogInput();
|
||||||
|
handle.writeLn("Analog input: " + value);
|
||||||
|
case "help":
|
||||||
|
handle.writeLn("Usage: redstone <on|off|get> <side>");
|
||||||
|
default:
|
||||||
|
handle.writeLn("Invalid subcommand");
|
||||||
|
return Future.sync(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Future.sync(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
166
src/bin/Terminal.hx
Normal file
166
src/bin/Terminal.hx
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
package bin;
|
||||||
|
|
||||||
|
import lib.ui.UIApp;
|
||||||
|
import lib.cli.TermHandle;
|
||||||
|
import lib.cli.CLIApp;
|
||||||
|
import lib.Color;
|
||||||
|
import kernel.ui.WindowContext;
|
||||||
|
import kernel.ui.WindowManager;
|
||||||
|
|
||||||
|
using tink.CoreApi;
|
||||||
|
|
||||||
|
class Terminal extends UIApp {
|
||||||
|
private var context:WindowContext;
|
||||||
|
private var input:String = "";
|
||||||
|
private var backlog:Array<String> = [];
|
||||||
|
private var exitTrigger: Bool -> Void;
|
||||||
|
|
||||||
|
public function new() {}
|
||||||
|
|
||||||
|
public function invoke(context: WindowContext): Future<Bool> {
|
||||||
|
this.context = context;
|
||||||
|
|
||||||
|
this.context.onChar.handle(char -> {
|
||||||
|
this.input += char;
|
||||||
|
this.redrawInput();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.context.onKey.handle(e -> {
|
||||||
|
if (e.keyCode == 259) {
|
||||||
|
this.input = this.input.substr(0, this.input.length - 1);
|
||||||
|
this.redrawInput();
|
||||||
|
} else if (e.keyCode == 257) {
|
||||||
|
this.backlog.push("> " + this.input);
|
||||||
|
var command = this.input;
|
||||||
|
this.input = "";
|
||||||
|
this.redrawBacklog();
|
||||||
|
this.redrawInput();
|
||||||
|
this.invokeCommand(command);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
WindowManager.instance.focusContextToOutput(context, "main");
|
||||||
|
this.redrawInput();
|
||||||
|
|
||||||
|
return new Future<Bool>(cb -> {
|
||||||
|
this.exitTrigger = cb;
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private function redrawBacklog() {
|
||||||
|
var size = this.context.getSize();
|
||||||
|
var linesAvailable = size.y - 1;
|
||||||
|
|
||||||
|
var start:Int = this.backlog.length - linesAvailable;
|
||||||
|
|
||||||
|
for (i in 0...linesAvailable) {
|
||||||
|
var line = this.backlog[start + i];
|
||||||
|
|
||||||
|
this.context.setCursorPos(0, i);
|
||||||
|
this.context.clearLine();
|
||||||
|
|
||||||
|
if (line != null) {
|
||||||
|
this.context.write(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.moveCursorToInput();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function redrawInput() {
|
||||||
|
var size = this.context.getSize();
|
||||||
|
|
||||||
|
this.context.setCursorPos(0, size.y - 1);
|
||||||
|
this.context.clearLine();
|
||||||
|
|
||||||
|
this.context.setTextColor(Color.Blue);
|
||||||
|
this.context.write("> ");
|
||||||
|
|
||||||
|
this.context.setTextColor(Color.White);
|
||||||
|
this.context.write(this.input);
|
||||||
|
|
||||||
|
this.context.setCursorBlink(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function invokeCommand(command:String):Void {
|
||||||
|
var args = this.parseArgs(command);
|
||||||
|
|
||||||
|
if (args.length == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var commandName = args[0];
|
||||||
|
|
||||||
|
// Handle built-in commands
|
||||||
|
switch (commandName) {
|
||||||
|
case "clear":
|
||||||
|
this.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var commandArgs:Array<String> = args.slice(1);
|
||||||
|
var hadInput = false;
|
||||||
|
var handle = new TermHandle(commandArgs, {
|
||||||
|
onWrite: (s:String) -> {
|
||||||
|
if (!hadInput) {
|
||||||
|
this.backlog.push("");
|
||||||
|
hadInput = true;
|
||||||
|
}
|
||||||
|
this.backlog[this.backlog.length - 1] += s;
|
||||||
|
this.redrawBacklog();
|
||||||
|
},
|
||||||
|
onNewLine: () -> {
|
||||||
|
this.backlog.push("");
|
||||||
|
this.redrawBacklog();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var prog:CLIApp = getProgByName(commandName);
|
||||||
|
|
||||||
|
if (prog == null) {
|
||||||
|
this.backlog.push("Command not found: " + commandName);
|
||||||
|
this.redrawBacklog();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.context.setCursorBlink(false);
|
||||||
|
|
||||||
|
prog.invoke(handle).handle((exitCode) -> {
|
||||||
|
// Cleanup extra newline
|
||||||
|
if (this.backlog[this.backlog.length - 1] == "") {
|
||||||
|
this.backlog.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.redrawInput();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private function parseArgs(command:String):Array<String> {
|
||||||
|
// TODO: tim and quote handling
|
||||||
|
return command.split(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
private function clear() {
|
||||||
|
this.backlog = [];
|
||||||
|
this.redrawBacklog();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getProgByName(name:String):CLIApp {
|
||||||
|
switch (name) {
|
||||||
|
case "hello":
|
||||||
|
return new HelloWorld();
|
||||||
|
case "net":
|
||||||
|
return new Net();
|
||||||
|
case "rs":
|
||||||
|
return new Redstone();
|
||||||
|
case "disk":
|
||||||
|
return new Disk();
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function moveCursorToInput() {
|
||||||
|
var size = this.context.getSize();
|
||||||
|
this.context.setCursorPos(this.input.length + 2, size.y - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
14
src/kernel/Entrypoint.hx
Normal file
14
src/kernel/Entrypoint.hx
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package kernel;
|
||||||
|
|
||||||
|
import kernel.log.Log;
|
||||||
|
|
||||||
|
class Entrypoint {
|
||||||
|
public static function main() {
|
||||||
|
Init.initKernel();
|
||||||
|
try {
|
||||||
|
Startup.main();
|
||||||
|
}catch(e){
|
||||||
|
Log.error('Error in startup: ${e.toString()}');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@ import kernel.turtle.Turtle;
|
|||||||
import haxe.MainLoop;
|
import haxe.MainLoop;
|
||||||
import kernel.net.Routing;
|
import kernel.net.Routing;
|
||||||
import cc.OS;
|
import cc.OS;
|
||||||
import util.Debug;
|
import lib.Debug;
|
||||||
import kernel.ui.WindowManager;
|
import kernel.ui.WindowManager;
|
||||||
import kernel.peripherals.Peripherals.Peripheral;
|
import kernel.peripherals.Peripherals.Peripheral;
|
||||||
import kernel.net.Net;
|
import kernel.net.Net;
|
||||||
@@ -17,7 +17,6 @@ class Init {
|
|||||||
|
|
||||||
WindowManager.instance = new WindowManager();
|
WindowManager.instance = new WindowManager();
|
||||||
MainTerm.instance = new MainTerm();
|
MainTerm.instance = new MainTerm();
|
||||||
Log.init();
|
|
||||||
|
|
||||||
if (Turtle.isTurtle()){
|
if (Turtle.isTurtle()){
|
||||||
Turtle.instance = new Turtle();
|
Turtle.instance = new Turtle();
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
package kernel;
|
package kernel;
|
||||||
|
|
||||||
import util.Pos;
|
import kernel.log.Log;
|
||||||
|
import lib.Pos;
|
||||||
import cc.HTTP.HTTPResponse;
|
import cc.HTTP.HTTPResponse;
|
||||||
import lua.TableTools;
|
import lua.TableTools;
|
||||||
import lua.Coroutine;
|
import lua.Coroutine;
|
||||||
import util.Vec.Vec2;
|
import lib.Vec.Vec2;
|
||||||
import haxe.Exception;
|
import haxe.Exception;
|
||||||
|
|
||||||
using tink.CoreApi;
|
using tink.CoreApi;
|
||||||
|
|||||||
@@ -1,80 +0,0 @@
|
|||||||
package kernel;
|
|
||||||
|
|
||||||
import kernel.ui.WindowContext;
|
|
||||||
import kernel.ui.WindowManager;
|
|
||||||
import kernel.ui.TermWriteable;
|
|
||||||
import lib.TermIO;
|
|
||||||
#if webconsole
|
|
||||||
import kernel.net.Net;
|
|
||||||
import util.Debug;
|
|
||||||
#end
|
|
||||||
|
|
||||||
/**
|
|
||||||
Log messages to specified output.
|
|
||||||
**/
|
|
||||||
class Log {
|
|
||||||
private static var context:WindowContext;
|
|
||||||
private static var writer:TermIO;
|
|
||||||
|
|
||||||
/**
|
|
||||||
Depends on: WindowManager
|
|
||||||
**/
|
|
||||||
@:allow(kernel.Init)
|
|
||||||
private static function init() {
|
|
||||||
Log.context = WindowManager.instance.createNewContext();
|
|
||||||
Log.writer = new TermIO(Log.context);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function setMainoutout(newOutput:TermWriteable) {
|
|
||||||
writer = new TermIO(newOutput);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function info(msg:Dynamic, ?pos:haxe.PosInfos) {
|
|
||||||
writer.writeLn(logLine("INFO",pos,msg));
|
|
||||||
#if webconsole
|
|
||||||
Debug.printWeb(logLine("INFO",pos,msg));
|
|
||||||
#end
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function warn(msg:Dynamic, ?pos:haxe.PosInfos) {
|
|
||||||
writer.writeLn(logLine("WARN",pos,msg), Yellow);
|
|
||||||
#if webconsole
|
|
||||||
Debug.printWeb(logLine("WARN",pos,msg));
|
|
||||||
#end
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function error(msg:Dynamic, ?pos:haxe.PosInfos) {
|
|
||||||
writer.writeLn(logLine("ERRO",pos,msg), Red);
|
|
||||||
#if webconsole
|
|
||||||
Debug.printWeb(logLine("ERRO",pos,msg));
|
|
||||||
#end
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function debug(msg:Dynamic, ?pos:haxe.PosInfos) {
|
|
||||||
#if debug
|
|
||||||
writer.writeLn(logLine("DEBG",pos,msg), Gray);
|
|
||||||
#if webconsole
|
|
||||||
Debug.printWeb(logLine("DEBG",pos,msg));
|
|
||||||
#end
|
|
||||||
#end
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function silly(msg:Dynamic, ?pos:haxe.PosInfos) {
|
|
||||||
writer.writeLn(logLine("SILY",pos,msg), LightGray);
|
|
||||||
#if webconsole
|
|
||||||
Debug.printWeb(logLine("SILY",pos,msg));
|
|
||||||
#end
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function moveToOutput(addr:String) {
|
|
||||||
WindowManager.instance.focusContextToOutput(context, addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function logLine(tag: String,pos: haxe.PosInfos,msg: Dynamic): String {
|
|
||||||
#if debug
|
|
||||||
return '[$tag][${pos.className}:${pos.lineNumber}]: ${Std.string(msg)}';
|
|
||||||
#else
|
|
||||||
return '[$tag]: ${Std.string(msg)}';
|
|
||||||
#end
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
package kernel;
|
package kernel;
|
||||||
|
|
||||||
import util.Pos;
|
import lib.Pos;
|
||||||
using tink.CoreApi;
|
|
||||||
|
|
||||||
import kernel.ui.TermWriteable;
|
import kernel.ui.TermWriteable;
|
||||||
import cc.Term;
|
import cc.Term;
|
||||||
import util.Vec.Vec2;
|
import lib.Vec.Vec2;
|
||||||
import util.Color;
|
import lib.Color;
|
||||||
|
|
||||||
|
using tink.CoreApi;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Represents the main computer screen.
|
Represents the main computer screen.
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
package kernel;
|
package kernel;
|
||||||
|
|
||||||
using tink.CoreApi;
|
|
||||||
|
|
||||||
import cc.OS;
|
import cc.OS;
|
||||||
|
|
||||||
|
using tink.CoreApi;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Wrapper class for using timer.
|
Wrapper class for using timer.
|
||||||
**/
|
**/
|
||||||
|
|||||||
48
src/kernel/log/Log.hx
Normal file
48
src/kernel/log/Log.hx
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
package kernel.log;
|
||||||
|
|
||||||
|
#if webconsole
|
||||||
|
import lib.Debug;
|
||||||
|
#end
|
||||||
|
|
||||||
|
/**
|
||||||
|
Log messages to specified output.
|
||||||
|
**/
|
||||||
|
class Log {
|
||||||
|
private static inline final MAX_LINES:Int = 100;
|
||||||
|
|
||||||
|
private static final logLines:Array<LogLine> = [];
|
||||||
|
|
||||||
|
public static function info(msg:Dynamic, ?pos:haxe.PosInfos) {
|
||||||
|
log({level: Info, message: Std.string(msg),time: 0});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function warn(msg:Dynamic, ?pos:haxe.PosInfos) {
|
||||||
|
log({level: Warn, message: Std.string(msg),time: 0});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function error(msg:Dynamic, ?pos:haxe.PosInfos) {
|
||||||
|
log({level: Error, message: Std.string(msg),time: 0});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function debug(msg:Dynamic, ?pos:haxe.PosInfos) {
|
||||||
|
#if debug
|
||||||
|
log({level: Debug, message: Std.string(msg),time: 0});
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function silly(msg:Dynamic, ?pos:haxe.PosInfos) {
|
||||||
|
log({level: Silly, message: Std.string(msg),time: 0});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function log(line: LogLine, ?pos:haxe.PosInfos) {
|
||||||
|
logLines.push(line);
|
||||||
|
|
||||||
|
if (logLines.length > MAX_LINES) {
|
||||||
|
logLines.shift();
|
||||||
|
}
|
||||||
|
|
||||||
|
#if webconsole
|
||||||
|
Debug.printWeb('[${Std.string(line.level)}] ${line.message}');
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
}
|
||||||
9
src/kernel/log/LogLevel.hx
Normal file
9
src/kernel/log/LogLevel.hx
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package kernel.log;
|
||||||
|
|
||||||
|
enum LogLevel {
|
||||||
|
Info;
|
||||||
|
Warn;
|
||||||
|
Error;
|
||||||
|
Debug;
|
||||||
|
Silly;
|
||||||
|
}
|
||||||
7
src/kernel/log/LogLine.hx
Normal file
7
src/kernel/log/LogLine.hx
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package kernel.log;
|
||||||
|
|
||||||
|
typedef LogLine = {
|
||||||
|
level: LogLevel,
|
||||||
|
message: String,
|
||||||
|
time: Int,
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package kernel.net;
|
package kernel.net;
|
||||||
|
|
||||||
|
import kernel.log.Log;
|
||||||
using tink.CoreApi;
|
using tink.CoreApi;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
package kernel.net;
|
package kernel.net;
|
||||||
|
|
||||||
using tink.CoreApi;
|
import haxe.ds.ReadOnlyArray;
|
||||||
|
|
||||||
import kernel.net.Package.NetworkID;
|
import kernel.net.Package.NetworkID;
|
||||||
import kernel.peripherals.Peripherals.Peripheral;
|
import kernel.peripherals.Peripherals.Peripheral;
|
||||||
import kernel.Log;
|
import kernel.log.Log;
|
||||||
import kernel.Timer;
|
import kernel.Timer;
|
||||||
import cc.OS;
|
import cc.OS;
|
||||||
|
|
||||||
|
using tink.CoreApi;
|
||||||
using Lambda;
|
using Lambda;
|
||||||
using util.Extender.LambdaExtender;
|
using lib.Extender.LambdaExtender;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Class responsible for everything network related.
|
Class responsible for everything network related.
|
||||||
@@ -25,7 +25,7 @@ class Net {
|
|||||||
public static inline final DEFAULT_TTL:Int = 10;
|
public static inline final DEFAULT_TTL:Int = 10;
|
||||||
|
|
||||||
public final networkID:NetworkID = OS.getComputerID();
|
public final networkID:NetworkID = OS.getComputerID();
|
||||||
private final responseBus:Map<Int, Callback<Package>> = new Map();
|
private final responseBus:Map<Int, Callback<Outcome<Package,Error>>> = new Map();
|
||||||
private final protoHandlers:Map<String, Callback<Package>> = new Map();
|
private final protoHandlers:Map<String, Callback<Package>> = new Map();
|
||||||
private var interfaces:Array<INetworkInterface>;
|
private var interfaces:Array<INetworkInterface>;
|
||||||
|
|
||||||
@@ -37,6 +37,26 @@ class Net {
|
|||||||
for (interf in interfaces){
|
for (interf in interfaces){
|
||||||
setupInterf(interf);
|
setupInterf(interf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setupPingHandle();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function setupPingHandle() {
|
||||||
|
this.registerProto("icmp", pack -> {
|
||||||
|
switch pack.data.type {
|
||||||
|
case "ping":
|
||||||
|
this.respondTo(pack, "pong");
|
||||||
|
case "died":
|
||||||
|
// If we get a "died" message from a node when one of our packages ttl hits 0
|
||||||
|
// the `data.msgId` prop is the message id
|
||||||
|
var msgID:Int = pack.data.msgID;
|
||||||
|
if (responseBus.exists(msgID)) {
|
||||||
|
responseBus[msgID].invoke(Outcome.Failure(new Error("TTL reached 0")));
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
Log.silly('Unknown icmp message: ${pack.data}');
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private function setupInterf(interf: INetworkInterface) {
|
private function setupInterf(interf: INetworkInterface) {
|
||||||
@@ -57,7 +77,7 @@ class Net {
|
|||||||
case Response:
|
case Response:
|
||||||
// Got a response to a send message. Invoke the callback
|
// Got a response to a send message. Invoke the callback
|
||||||
if (responseBus.exists(pack.msgID)) {
|
if (responseBus.exists(pack.msgID)) {
|
||||||
responseBus[pack.msgID].invoke(pack);
|
responseBus[pack.msgID].invoke(Outcome.Success(pack));
|
||||||
}
|
}
|
||||||
case RouteDiscover(_) | RouteDiscoverResponse(_) | RouteDiscoverUpdate(_):
|
case RouteDiscover(_) | RouteDiscoverResponse(_) | RouteDiscoverUpdate(_):
|
||||||
// Delegate to Routing
|
// Delegate to Routing
|
||||||
@@ -91,6 +111,13 @@ class Net {
|
|||||||
|
|
||||||
private function forwardPackage(pack: Package) {
|
private function forwardPackage(pack: Package) {
|
||||||
if (pack.ttl == 0){
|
if (pack.ttl == 0){
|
||||||
|
|
||||||
|
if (pack.type.match(Data(_))) {
|
||||||
|
// If the package is a data package and the ttl hits 0
|
||||||
|
// we send a "died" message to the sender
|
||||||
|
sendAndForget(pack.fromID, "icmp", {type:"died", msgID: pack.msgID});
|
||||||
|
}
|
||||||
|
|
||||||
// Drop package
|
// Drop package
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -164,8 +191,16 @@ class Net {
|
|||||||
|
|
||||||
var timeout:Timer = null;
|
var timeout:Timer = null;
|
||||||
|
|
||||||
responseBus[pack.msgID] = ((reponse:Package) -> {
|
responseBus[pack.msgID] = ((reponse:Outcome<Package,Error>) -> {
|
||||||
resolve(reponse);
|
|
||||||
|
switch reponse {
|
||||||
|
case Success(pack):
|
||||||
|
resolve(pack);
|
||||||
|
case Failure(err):
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always remove the timeout
|
||||||
if (timeout != null) {
|
if (timeout != null) {
|
||||||
timeout.cancle();
|
timeout.cancle();
|
||||||
}
|
}
|
||||||
@@ -198,4 +233,31 @@ class Net {
|
|||||||
public function removeProto(proto:String) {
|
public function removeProto(proto:String) {
|
||||||
protoHandlers.remove(proto);
|
protoHandlers.remove(proto);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Sends a ping package to the given id. Returns true if there was a response.
|
||||||
|
**/
|
||||||
|
public function ping(toID: NetworkID): Promise<Noise> {
|
||||||
|
return new Promise<Noise>((resolve,reject)->{
|
||||||
|
this.sendAndAwait(toID,"icmp",{type:"ping"}).handle(pack -> {
|
||||||
|
switch pack {
|
||||||
|
case Success(_):
|
||||||
|
resolve(Noise);
|
||||||
|
case Failure(err):
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getActiveProtocols(): ReadOnlyArray<String> {
|
||||||
|
var arr = new Array<String>();
|
||||||
|
|
||||||
|
for (proto in protoHandlers.keys()) {
|
||||||
|
arr.push(proto);
|
||||||
|
}
|
||||||
|
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
package kernel.net;
|
package kernel.net;
|
||||||
|
|
||||||
|
import kernel.log.Log;
|
||||||
import kernel.peripherals.Peripherals.Peripheral;
|
import kernel.peripherals.Peripherals.Peripheral;
|
||||||
import kernel.net.INetworkInterface;
|
import kernel.net.INetworkInterface;
|
||||||
|
import kernel.net.Package.NetworkID;
|
||||||
|
|
||||||
using tink.CoreApi;
|
using tink.CoreApi;
|
||||||
|
|
||||||
import kernel.net.Package.NetworkID;
|
|
||||||
|
|
||||||
@:structInit typedef Route = {
|
@:structInit typedef Route = {
|
||||||
interf:INetworkInterface,
|
interf:INetworkInterface,
|
||||||
rep: NetworkID,
|
rep:NetworkID,
|
||||||
cost:Int
|
cost:Int
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22,13 +22,13 @@ class Routing {
|
|||||||
**/
|
**/
|
||||||
public static var instance:Routing;
|
public static var instance:Routing;
|
||||||
|
|
||||||
public static inline final UPDATE_WAIT_TIME:Float = 3;
|
public static inline final UPDATE_WAIT_TIME:Float = 1;
|
||||||
|
|
||||||
public final onNewNeigbor:Signal<Int>;
|
public final onNewNeigbor:Signal<Int>;
|
||||||
|
|
||||||
private final onNewNeigborTrigger:SignalTrigger<NetworkID> = Signal.trigger();
|
private final onNewNeigborTrigger:SignalTrigger<NetworkID> = Signal.trigger();
|
||||||
private final routingTable:Map<NetworkID, Route> = new Map();
|
private final routingTable:Map<NetworkID, Route> = new Map();
|
||||||
private var routeUpdateInQueue: Bool = false;
|
private var routeUpdateInQueue:Bool = false;
|
||||||
|
|
||||||
@:allow(kernel.Init)
|
@:allow(kernel.Init)
|
||||||
private function new() {
|
private function new() {
|
||||||
@@ -37,61 +37,59 @@ class Routing {
|
|||||||
|
|
||||||
@:allow(kernel.Init)
|
@:allow(kernel.Init)
|
||||||
private function init() {
|
private function init() {
|
||||||
routingTable.set(Net.instance.networkID,{interf: Loopback.instance,cost:0,rep:Net.instance.networkID});
|
routingTable.set(Net.instance.networkID, {interf: Loopback.instance, cost: 0, rep: Net.instance.networkID});
|
||||||
for (modem in Peripheral.instance.getModems()) {
|
brodcastRoutingTable();
|
||||||
modem.send(Net.BRODCAST_PORT, Net.instance.networkID, newRoutDiscoverPackage());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Prepares an brodcast of the current routing table. If a brodcast is already in queue it will be ignored.
|
||||||
|
After UPDATE_WAIT_TIME seconds the routing table will be brodcasted. This is done to prevent spamming the network.
|
||||||
|
**/
|
||||||
private function prepareRouteUpdate() {
|
private function prepareRouteUpdate() {
|
||||||
if (this.routeUpdateInQueue){
|
if (this.routeUpdateInQueue) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.routeUpdateInQueue = true;
|
this.routeUpdateInQueue = true;
|
||||||
|
|
||||||
new Timer(UPDATE_WAIT_TIME,()->{
|
new Timer(UPDATE_WAIT_TIME, () -> {
|
||||||
brodcastUpdatedRoutes();
|
brodcastRoutingTable();
|
||||||
this.routeUpdateInQueue = false;
|
this.routeUpdateInQueue = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private function brodcastUpdatedRoutes() {
|
/**
|
||||||
var pack: Package = {
|
Brodcast the current routing table to all peers.
|
||||||
type: RouteDiscoverUpdate(genRouteList()),
|
**/
|
||||||
toID: Net.BRODCAST_PORT,
|
private function brodcastRoutingTable() {
|
||||||
msgID: null,
|
var pack = newRoutDiscoverPackage();
|
||||||
fromID: Net.instance.networkID,
|
|
||||||
data: null,
|
|
||||||
ttl: 0, // Prevent forwarding
|
|
||||||
};
|
|
||||||
|
|
||||||
for (modem in Peripheral.instance.getModems()) {
|
for (modem in Peripheral.instance.getModems()) {
|
||||||
|
modem.send(Net.BRODCAST_PORT, Net.instance.networkID, pack);
|
||||||
modem.send(Net.BRODCAST_PORT, Net.instance.networkID,pack);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Handle incomming packages that involve route discovery.
|
Handle incomming packages that involve route discovery.
|
||||||
**/
|
**/
|
||||||
public function handleRoutePackage(pack:Package, interf:INetworkInterface):Void {
|
@:allow(kernel.net.Net)
|
||||||
addPossibleRoute(pack.fromID,interf,0,pack.fromID);
|
private function handleRoutePackage(pack:Package, interf:INetworkInterface):Void {
|
||||||
|
addPossibleRoute(pack.fromID, interf, 0, pack.fromID);
|
||||||
|
|
||||||
var shouldRespond: Bool = switch pack.type {
|
var shouldRespond:Bool = switch pack.type {
|
||||||
case RouteDiscoverResponse(routes):
|
case RouteDiscoverResponse(routes):
|
||||||
for (route in routes) {
|
for (route in routes) {
|
||||||
addPossibleRoute(route.id,interf,route.cost,pack.fromID);
|
addPossibleRoute(route.id, interf, route.cost, pack.fromID);
|
||||||
}
|
}
|
||||||
false;
|
false;
|
||||||
case RouteDiscover(routes):
|
case RouteDiscover(routes):
|
||||||
for (route in routes) {
|
for (route in routes) {
|
||||||
addPossibleRoute(route.id,interf,route.cost,pack.fromID);
|
addPossibleRoute(route.id, interf, route.cost, pack.fromID);
|
||||||
}
|
}
|
||||||
true;
|
true;
|
||||||
case RouteDiscoverUpdate(routes):
|
case RouteDiscoverUpdate(routes):
|
||||||
for (route in routes) {
|
for (route in routes) {
|
||||||
addPossibleRoute(route.id,interf,route.cost,pack.fromID);
|
addPossibleRoute(route.id, interf, route.cost, pack.fromID);
|
||||||
}
|
}
|
||||||
false;
|
false;
|
||||||
default:
|
default:
|
||||||
@@ -99,7 +97,7 @@ class Routing {
|
|||||||
false;
|
false;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!shouldRespond){
|
if (!shouldRespond) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,12 +111,15 @@ class Routing {
|
|||||||
ttl: 0, // Prevent forwarding
|
ttl: 0, // Prevent forwarding
|
||||||
}
|
}
|
||||||
|
|
||||||
interf.send(response.toID,Net.instance.networkID,response);
|
interf.send(response.toID, Net.instance.networkID, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function genRouteList(): Array<{id:NetworkID,cost:Int}> {
|
/**
|
||||||
var routes: Array<{id:NetworkID,cost:Int}> = [];
|
Generate a list of routes to send to a peer based on the current routing table.
|
||||||
for (k => v in this.routingTable){
|
**/
|
||||||
|
private function genRouteList():Array<{id:NetworkID, cost:Int}> {
|
||||||
|
var routes:Array<{id:NetworkID, cost:Int}> = [];
|
||||||
|
for (k => v in this.routingTable) {
|
||||||
routes.push({
|
routes.push({
|
||||||
id: k,
|
id: k,
|
||||||
cost: v.cost
|
cost: v.cost
|
||||||
@@ -144,36 +145,40 @@ class Routing {
|
|||||||
Called when a route to a client has been disoverd.
|
Called when a route to a client has been disoverd.
|
||||||
Its possible to be called multiple times with the same id but different addr.
|
Its possible to be called multiple times with the same id but different addr.
|
||||||
**/
|
**/
|
||||||
private function addPossibleRoute(toID:Int, interf:INetworkInterface, cost:Int, rep: NetworkID) {
|
private function addPossibleRoute(toID:Int, interf:INetworkInterface, cost:Int, rep:NetworkID) {
|
||||||
if (toID == Net.instance.networkID){
|
if (toID == Net.instance.networkID) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var fullCost = cost+interf.getBaseRoutingCost();
|
var fullCost = cost + interf.getBaseRoutingCost();
|
||||||
|
|
||||||
if (this.routingTable.exists(toID)){
|
if (this.routingTable.exists(toID)) {
|
||||||
if (this.routingTable[toID].cost > fullCost){
|
if (this.routingTable[toID].cost > fullCost) {
|
||||||
this.routingTable[toID] = {interf:interf,cost:cost + interf.getBaseRoutingCost(),rep: rep};
|
this.routingTable[toID] = {interf: interf, cost: cost + interf.getBaseRoutingCost(), rep: rep};
|
||||||
this.prepareRouteUpdate();
|
this.prepareRouteUpdate();
|
||||||
}
|
}
|
||||||
}else{
|
} else {
|
||||||
this.routingTable[toID] = {interf:interf,cost:cost + interf.getBaseRoutingCost(),rep: rep};
|
this.routingTable[toID] = {interf: interf, cost: cost + interf.getBaseRoutingCost(), rep: rep};
|
||||||
this.onNewNeigborTrigger.trigger(toID);
|
this.onNewNeigborTrigger.trigger(toID);
|
||||||
this.prepareRouteUpdate();
|
this.prepareRouteUpdate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getRouteToID(networkID:NetworkID):{interf: INetworkInterface, rep: NetworkID} {
|
public function getRouteToID(networkID:NetworkID):{interf:INetworkInterface, rep:NetworkID} {
|
||||||
if (networkID == Net.instance.networkID) {
|
if (networkID == Net.instance.networkID) {
|
||||||
return {interf: Loopback.instance,rep: Net.instance.networkID};
|
return {interf: Loopback.instance, rep: Net.instance.networkID};
|
||||||
}
|
}
|
||||||
|
|
||||||
var route = this.routingTable[networkID];
|
var route:Null<Route> = this.routingTable[networkID];
|
||||||
|
|
||||||
if (route == null){
|
if (route == null) {
|
||||||
return null;
|
return null;
|
||||||
}else{
|
} else {
|
||||||
return {interf: route.interf,rep: route.rep};
|
return {interf: route.interf, rep: route.rep};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getRouteTable():Map<NetworkID, Route> {
|
||||||
|
return this.routingTable;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package kernel.peripherals;
|
package kernel.peripherals;
|
||||||
|
|
||||||
import kernel.net.Package;
|
|
||||||
using tink.CoreApi;
|
using tink.CoreApi;
|
||||||
|
|
||||||
class Drive implements IPeripheral {
|
class Drive implements IPeripheral {
|
||||||
@@ -52,8 +51,13 @@ class Drive implements IPeripheral {
|
|||||||
this.native.setDiskLabel();
|
this.native.setDiskLabel();
|
||||||
}
|
}
|
||||||
|
|
||||||
public inline function setDiskLabel(label: String) {
|
public inline function setDiskLabel(label: String): Null<Error> {
|
||||||
|
try {
|
||||||
this.native.setDiskLabel(label);
|
this.native.setDiskLabel(label);
|
||||||
|
return null;
|
||||||
|
} catch (e: Dynamic) {
|
||||||
|
return new Error("Invalid label");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public inline function hasData():Bool {
|
public inline function hasData():Bool {
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
package kernel.peripherals;
|
package kernel.peripherals;
|
||||||
|
|
||||||
using tink.CoreApi;
|
|
||||||
|
|
||||||
import kernel.net.Package;
|
import kernel.net.Package;
|
||||||
import kernel.net.INetworkInterface;
|
import kernel.net.INetworkInterface;
|
||||||
|
|
||||||
|
using tink.CoreApi;
|
||||||
|
|
||||||
class Modem implements INetworkInterface implements IPeripheral {
|
class Modem implements INetworkInterface implements IPeripheral {
|
||||||
public final addr:String;
|
public final addr:String;
|
||||||
public var onMessage(default, null):Signal<Package>;
|
public var onMessage(default, null):Signal<Package>;
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ class Peripheral {
|
|||||||
private var modes: Array<Modem> = [];
|
private var modes: Array<Modem> = [];
|
||||||
private var drives:Array<Drive> = [];
|
private var drives:Array<Drive> = [];
|
||||||
private var redstone:Array<Redstone> = [];
|
private var redstone:Array<Redstone> = [];
|
||||||
|
private var printers:Array<Printer> = [];
|
||||||
|
|
||||||
@:allow(kernel.Init)
|
@:allow(kernel.Init)
|
||||||
private function new() {
|
private function new() {
|
||||||
@@ -85,7 +86,24 @@ class Peripheral {
|
|||||||
this.drives = allDrives.map(s -> return new Drive((cc.Peripheral.wrap(s) : Dynamic), s));
|
this.drives = allDrives.map(s -> return new Drive((cc.Peripheral.wrap(s) : Dynamic), s));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Get redstone peripheral.
|
||||||
|
**/
|
||||||
public function getRedstone(side: String): Redstone {
|
public function getRedstone(side: String): Redstone {
|
||||||
return this.redstone.find(item -> item.getAddr() == side);
|
return this.redstone.find(item -> item.getAddr() == side);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Get all connected printers.
|
||||||
|
**/
|
||||||
|
|
||||||
|
public function getPrinters():ReadOnlyArray<Printer> {
|
||||||
|
return this.printers;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function findPrinters() {
|
||||||
|
var allPrinters = cc.Peripheral.getNames().toArray().filter(s -> cc.Peripheral.getType(s) == "printer");
|
||||||
|
this.printers = allPrinters.map(s -> return new Printer((cc.Peripheral.wrap(s) : Dynamic), s));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
56
src/kernel/peripherals/Printer.hx
Normal file
56
src/kernel/peripherals/Printer.hx
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
package kernel.peripherals;
|
||||||
|
|
||||||
|
import lib.Rect;
|
||||||
|
import lib.Pos;
|
||||||
|
|
||||||
|
class Printer implements IPeripheral {
|
||||||
|
|
||||||
|
private final native:cc.periphs.Printer.Printer;
|
||||||
|
private final addr:String;
|
||||||
|
|
||||||
|
public function new(native: cc.periphs.Printer.Printer, addr: String) {
|
||||||
|
this.native = native;
|
||||||
|
this.addr = addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAddr():String {
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function write(text: String){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCurserPos(): Pos {
|
||||||
|
return new Pos({x: 0, y: 0});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setCurserPos(pos: Pos){
|
||||||
|
this.native.setCursorPos(pos.x, pos.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPageSize(): Rect {
|
||||||
|
var pos = this.native.getPageSize();
|
||||||
|
return new Rect({x: 0, y: 0}, {x: pos.x, y: pos.y});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function newPage(): Bool{
|
||||||
|
return this.native.newPage();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function endPage(): Bool{
|
||||||
|
return this.native.endPage();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPageTitle(title: String){
|
||||||
|
this.native.setPageTitle(title);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getInkLevel(): Float{
|
||||||
|
return this.native.getInkLevel();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPaperLevel():Int {
|
||||||
|
return this.native.getPaperLevel();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
package kernel.peripherals;
|
package kernel.peripherals;
|
||||||
|
|
||||||
import haxe.ds.ReadOnlyArray;
|
import haxe.ds.ReadOnlyArray;
|
||||||
import util.Color;
|
import lib.Color;
|
||||||
|
|
||||||
using tink.CoreApi;
|
using tink.CoreApi;
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
package kernel.peripherals;
|
package kernel.peripherals;
|
||||||
|
|
||||||
|
import lib.Pos;
|
||||||
import util.Pos;
|
|
||||||
import cc.Term.TerminalSize;
|
import cc.Term.TerminalSize;
|
||||||
import kernel.ui.TermWriteable;
|
import kernel.ui.TermWriteable;
|
||||||
import util.Vec.Vec2;
|
import lib.Vec.Vec2;
|
||||||
import util.Color;
|
import lib.Color;
|
||||||
|
|
||||||
using tink.CoreApi;
|
using tink.CoreApi;
|
||||||
|
|
||||||
|
|||||||
@@ -7,4 +7,24 @@ enum abstract Side(String) to String {
|
|||||||
var Right = "right";
|
var Right = "right";
|
||||||
var Front = "front";
|
var Front = "front";
|
||||||
var Back = "back";
|
var Back = "back";
|
||||||
|
|
||||||
|
@:from
|
||||||
|
static public function fromString(s: String) {
|
||||||
|
switch (s) {
|
||||||
|
case "top":
|
||||||
|
return Top;
|
||||||
|
case "bottom":
|
||||||
|
return Bottom;
|
||||||
|
case "left":
|
||||||
|
return Left;
|
||||||
|
case "right":
|
||||||
|
return Right;
|
||||||
|
case "front":
|
||||||
|
return Front;
|
||||||
|
case "back":
|
||||||
|
return Back;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package kernel.turtle;
|
package kernel.turtle;
|
||||||
|
|
||||||
|
import kernel.log.Log;
|
||||||
import kernel.turtle.Types;
|
import kernel.turtle.Types;
|
||||||
|
|
||||||
using tink.CoreApi;
|
using tink.CoreApi;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package kernel.ui;
|
package kernel.ui;
|
||||||
|
|
||||||
import util.Color;
|
import lib.Color;
|
||||||
|
|
||||||
@:structInit class Pixel {
|
@:structInit class Pixel {
|
||||||
public var char:String;
|
public var char:String;
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
package kernel.ui;
|
package kernel.ui;
|
||||||
|
|
||||||
|
import lib.Pos;
|
||||||
import util.Pos;
|
import lib.Vec.Vec2;
|
||||||
import util.Vec.Vec2;
|
import lib.Color;
|
||||||
import util.Color;
|
|
||||||
import kernel.ui.TermWriteable;
|
import kernel.ui.TermWriteable;
|
||||||
|
|
||||||
using tink.CoreApi;
|
using tink.CoreApi;
|
||||||
@@ -89,6 +88,7 @@ class TermBuffer implements TermWriteable {
|
|||||||
target.setCursorPos(cursorPos.x, cursorPos.y);
|
target.setCursorPos(cursorPos.x, cursorPos.y);
|
||||||
target.setTextColor(currentTextColor);
|
target.setTextColor(currentTextColor);
|
||||||
target.setBackgroundColor(currentBgColor);
|
target.setBackgroundColor(currentBgColor);
|
||||||
|
target.setCursorBlink(cursorBlink);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function safeWriteScreenBuffer(pos:Pos, char:String) {
|
private function safeWriteScreenBuffer(pos:Pos, char:String) {
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
package kernel.ui;
|
package kernel.ui;
|
||||||
|
|
||||||
import util.Pos;
|
import lib.Pos;
|
||||||
using tink.CoreApi;
|
import lib.Color;
|
||||||
|
import lib.Vec.Vec2;
|
||||||
|
|
||||||
import util.Color;
|
using tink.CoreApi;
|
||||||
import util.Vec.Vec2;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Interface describing a terminal. E.g. the main computer screen or a external screen.
|
Interface describing a terminal. E.g. the main computer screen or a external screen.
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
package kernel.ui;
|
package kernel.ui;
|
||||||
|
|
||||||
import util.Pos;
|
import lib.Pos;
|
||||||
using tink.CoreApi;
|
import lib.Vec.Vec2;
|
||||||
|
import lib.Color;
|
||||||
import util.Vec.Vec2;
|
|
||||||
import util.Color;
|
|
||||||
import kernel.ui.TermWriteable;
|
import kernel.ui.TermWriteable;
|
||||||
|
|
||||||
|
using tink.CoreApi;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
A term writer that can switch beetween its internal buffer and another termwriter.
|
A term writer that can switch beetween its internal buffer and another termwriter.
|
||||||
The other target is most of the time a real screen
|
The other target is most of the time a real screen
|
||||||
|
|||||||
@@ -1,19 +1,22 @@
|
|||||||
package kernel.ui;
|
package kernel.ui;
|
||||||
|
|
||||||
import util.Pos;
|
import lib.ui.rendere.UIEventDelegate;
|
||||||
using tink.CoreApi;
|
import lib.Pos;
|
||||||
|
import lib.Color;
|
||||||
import util.Color;
|
|
||||||
import kernel.ButtonType;
|
import kernel.ButtonType;
|
||||||
import util.Vec.Vec2;
|
import lib.Vec.Vec2;
|
||||||
import kernel.ui.TermWriteable;
|
import kernel.ui.TermWriteable;
|
||||||
|
|
||||||
|
using tink.CoreApi;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
The main object you interact with when writing anything to the screen.
|
The main object you interact with when writing anything to the screen.
|
||||||
**/
|
**/
|
||||||
class WindowContext implements TermWriteable {
|
class WindowContext implements TermWriteable {
|
||||||
private final writer:VirtualTermWriter;
|
private final writer:VirtualTermWriter;
|
||||||
|
|
||||||
|
@:allow(kernel.ui.WindowManager) private var eventDelegate: Null<UIEventDelegate>;
|
||||||
|
|
||||||
public var onClick(default, null):Signal<{button:ButtonType, pos:Pos}>;
|
public var onClick(default, null):Signal<{button:ButtonType, pos:Pos}>;
|
||||||
public var onKey(default, null):Signal<{keyCode:Int, isHeld:Bool}>;
|
public var onKey(default, null):Signal<{keyCode:Int, isHeld:Bool}>;
|
||||||
public var onKeyUp(default, null):Signal<Int>;
|
public var onKeyUp(default, null):Signal<Int>;
|
||||||
@@ -21,6 +24,7 @@ class WindowContext implements TermWriteable {
|
|||||||
public var onMouseScroll(default, null):Signal<{dir:Int, pos:Pos}>;
|
public var onMouseScroll(default, null):Signal<{dir:Int, pos:Pos}>;
|
||||||
public var onMouseUp(default, null):Signal<{button:ButtonType, pos:Pos}>;
|
public var onMouseUp(default, null):Signal<{button:ButtonType, pos:Pos}>;
|
||||||
public var onPaste(default, null):Signal<String>;
|
public var onPaste(default, null):Signal<String>;
|
||||||
|
public var onChar(default, null):Signal<String>;
|
||||||
|
|
||||||
@:allow(kernel.ui.WindowManager) private final onClickTrigger:SignalTrigger<{button:ButtonType, pos:Pos}>;
|
@:allow(kernel.ui.WindowManager) private final onClickTrigger:SignalTrigger<{button:ButtonType, pos:Pos}>;
|
||||||
@:allow(kernel.ui.WindowManager) private final onKeyTrigger:SignalTrigger<{keyCode:Int, isHeld:Bool}>;
|
@:allow(kernel.ui.WindowManager) private final onKeyTrigger:SignalTrigger<{keyCode:Int, isHeld:Bool}>;
|
||||||
@@ -29,6 +33,7 @@ class WindowContext implements TermWriteable {
|
|||||||
@:allow(kernel.ui.WindowManager) private final onMouseScrollTrigger:SignalTrigger<{dir:Int, pos:Pos}>;
|
@:allow(kernel.ui.WindowManager) private final onMouseScrollTrigger:SignalTrigger<{dir:Int, pos:Pos}>;
|
||||||
@:allow(kernel.ui.WindowManager) private final onMouseUpTrigger:SignalTrigger<{button:ButtonType, pos:Pos}>;
|
@:allow(kernel.ui.WindowManager) private final onMouseUpTrigger:SignalTrigger<{button:ButtonType, pos:Pos}>;
|
||||||
@:allow(kernel.ui.WindowManager) private final onPasteTrigger:SignalTrigger<String>;
|
@:allow(kernel.ui.WindowManager) private final onPasteTrigger:SignalTrigger<String>;
|
||||||
|
@:allow(kernel.ui.WindowManager) private final onCharTrigger:SignalTrigger<String>;
|
||||||
|
|
||||||
@:allow(kernel.ui.WindowManager)
|
@:allow(kernel.ui.WindowManager)
|
||||||
private function new(writer:VirtualTermWriter) {
|
private function new(writer:VirtualTermWriter) {
|
||||||
@@ -42,6 +47,7 @@ class WindowContext implements TermWriteable {
|
|||||||
this.onMouseScrollTrigger = Signal.trigger();
|
this.onMouseScrollTrigger = Signal.trigger();
|
||||||
this.onMouseUpTrigger = Signal.trigger();
|
this.onMouseUpTrigger = Signal.trigger();
|
||||||
this.onPasteTrigger = Signal.trigger();
|
this.onPasteTrigger = Signal.trigger();
|
||||||
|
this.onCharTrigger = Signal.trigger();
|
||||||
|
|
||||||
this.onClick = onClickTrigger.asSignal();
|
this.onClick = onClickTrigger.asSignal();
|
||||||
this.onKey = onKeyTrigger.asSignal();
|
this.onKey = onKeyTrigger.asSignal();
|
||||||
@@ -50,6 +56,7 @@ class WindowContext implements TermWriteable {
|
|||||||
this.onMouseScroll = onMouseScrollTrigger.asSignal();
|
this.onMouseScroll = onMouseScrollTrigger.asSignal();
|
||||||
this.onMouseUp = onMouseUpTrigger.asSignal();
|
this.onMouseUp = onMouseUpTrigger.asSignal();
|
||||||
this.onPaste = onPasteTrigger.asSignal();
|
this.onPaste = onPasteTrigger.asSignal();
|
||||||
|
this.onChar = onCharTrigger.asSignal();
|
||||||
}
|
}
|
||||||
|
|
||||||
public var onResize(default, null):Signal<Vec2<Int>>;
|
public var onResize(default, null):Signal<Vec2<Int>>;
|
||||||
@@ -133,4 +140,12 @@ class WindowContext implements TermWriteable {
|
|||||||
public function reset() {
|
public function reset() {
|
||||||
writer.reset();
|
writer.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Delegate events to an UIEventDelegate.
|
||||||
|
Set to null to stop delegating events.
|
||||||
|
**/
|
||||||
|
public function delegateEvents(delegate: Null<UIEventDelegate>){
|
||||||
|
this.eventDelegate = delegate;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,63 +14,106 @@ class WindowManager {
|
|||||||
**/
|
**/
|
||||||
public static var instance:WindowManager;
|
public static var instance:WindowManager;
|
||||||
private var currentMainContext:WindowContext;
|
private var currentMainContext:WindowContext;
|
||||||
private final allContexts:Array<WindowContext> = new Array();
|
|
||||||
private final outputMap:Map<String, WindowContext> = new Map();
|
private final outputMap:Map<String, WindowContext> = new Map();
|
||||||
|
|
||||||
@:allow(kernel.Init)
|
@:allow(kernel.Init)
|
||||||
private function new() {
|
private function new() {
|
||||||
KernelEvents.instance.onKey.handle(params -> {
|
KernelEvents.instance.onKey.handle(params -> {
|
||||||
if (currentMainContext != null) {
|
if (currentMainContext != null) {
|
||||||
|
if (currentMainContext.eventDelegate != null){
|
||||||
|
var foo = currentMainContext.eventDelegate.getEventHandlers();
|
||||||
|
foo.onKey != null ? foo.onKey.invoke(params) : null;
|
||||||
|
}else{
|
||||||
currentMainContext.onKeyTrigger.trigger(params);
|
currentMainContext.onKeyTrigger.trigger(params);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
KernelEvents.instance.onKeyUp.handle(keyCode -> {
|
KernelEvents.instance.onKeyUp.handle(keyCode -> {
|
||||||
if (currentMainContext != null) {
|
if (currentMainContext != null) {
|
||||||
|
if (currentMainContext.eventDelegate != null){
|
||||||
|
var foo = currentMainContext.eventDelegate.getEventHandlers();
|
||||||
|
foo.onKeyUp != null ? foo.onKeyUp.invoke(keyCode) : null;
|
||||||
|
}else{
|
||||||
currentMainContext.onKeyUpTrigger.trigger(keyCode);
|
currentMainContext.onKeyUpTrigger.trigger(keyCode);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
KernelEvents.instance.onMouseClick.handle(params -> {
|
KernelEvents.instance.onMouseClick.handle(params -> {
|
||||||
if (currentMainContext != null) {
|
if (currentMainContext != null) {
|
||||||
|
if (currentMainContext.eventDelegate != null){
|
||||||
|
var foo = currentMainContext.eventDelegate.getEventHandlers();
|
||||||
|
foo.onClick != null ? foo.onClick.invoke(params) : null;
|
||||||
|
}else{
|
||||||
currentMainContext.onClickTrigger.trigger(params);
|
currentMainContext.onClickTrigger.trigger(params);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
KernelEvents.instance.onMouseDrag.handle(params -> {
|
KernelEvents.instance.onMouseDrag.handle(params -> {
|
||||||
if (currentMainContext != null) {
|
if (currentMainContext != null) {
|
||||||
|
if (currentMainContext.eventDelegate != null){
|
||||||
|
var foo = currentMainContext.eventDelegate.getEventHandlers();
|
||||||
|
foo.onMouseDrag != null ? foo.onMouseDrag.invoke(params) : null;
|
||||||
|
}else{
|
||||||
currentMainContext.onMouseDragTrigger.trigger(params);
|
currentMainContext.onMouseDragTrigger.trigger(params);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
KernelEvents.instance.onMouseScroll.handle(params -> {
|
KernelEvents.instance.onMouseScroll.handle(params -> {
|
||||||
if (currentMainContext != null) {
|
if (currentMainContext != null) {
|
||||||
|
if (currentMainContext.eventDelegate != null){
|
||||||
|
var foo = currentMainContext.eventDelegate.getEventHandlers();
|
||||||
|
foo.onMouseScroll != null ? foo.onMouseScroll.invoke(params) : null;
|
||||||
|
}else{
|
||||||
currentMainContext.onMouseScrollTrigger.trigger(params);
|
currentMainContext.onMouseScrollTrigger.trigger(params);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
KernelEvents.instance.onMouseUp.handle(params -> {
|
KernelEvents.instance.onMouseUp.handle(params -> {
|
||||||
if (currentMainContext != null) {
|
if (currentMainContext != null) {
|
||||||
|
if (currentMainContext.eventDelegate != null){
|
||||||
|
var foo = currentMainContext.eventDelegate.getEventHandlers();
|
||||||
|
foo.onMouseUp != null ? foo.onMouseUp.invoke(params) : null;
|
||||||
|
}else{
|
||||||
currentMainContext.onMouseUpTrigger.trigger(params);
|
currentMainContext.onMouseUpTrigger.trigger(params);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
KernelEvents.instance.onPaste.handle(text -> {
|
KernelEvents.instance.onPaste.handle(text -> {
|
||||||
if (currentMainContext != null) {
|
if (currentMainContext != null) {
|
||||||
|
if (currentMainContext.eventDelegate != null){
|
||||||
|
var foo = currentMainContext.eventDelegate.getEventHandlers();
|
||||||
|
foo.onPaste != null ? foo.onPaste.invoke(text) : null;
|
||||||
|
}else{
|
||||||
currentMainContext.onPasteTrigger.trigger(text);
|
currentMainContext.onPasteTrigger.trigger(text);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
KernelEvents.instance.onMonitorTouch.handle(params -> {
|
KernelEvents.instance.onMonitorTouch.handle(params -> {
|
||||||
// TODO
|
// TODO
|
||||||
});
|
});
|
||||||
|
|
||||||
|
KernelEvents.instance.onChar.handle(char -> {
|
||||||
|
if (currentMainContext != null) {
|
||||||
|
if (currentMainContext.eventDelegate != null){
|
||||||
|
var foo = currentMainContext.eventDelegate.getEventHandlers();
|
||||||
|
foo.onChar != null ? foo.onChar.invoke(char) : null;
|
||||||
|
}else{
|
||||||
|
currentMainContext.onCharTrigger.trigger(char);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public function createNewContext():WindowContext {
|
public function createNewContext():WindowContext {
|
||||||
var newContext = new WindowContext(new VirtualTermWriter());
|
var newContext = new WindowContext(new VirtualTermWriter());
|
||||||
|
|
||||||
allContexts.push(newContext);
|
|
||||||
|
|
||||||
return newContext;
|
return newContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,6 +123,9 @@ class WindowManager {
|
|||||||
return arr;
|
return arr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Move context to output. If output is "main", context will be moved to main screen.
|
||||||
|
**/
|
||||||
public function focusContextToOutput(context:WindowContext, output:String) {
|
public function focusContextToOutput(context:WindowContext, output:String) {
|
||||||
var target:TermWriteable;
|
var target:TermWriteable;
|
||||||
if (output == "main") {
|
if (output == "main") {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package util;
|
package lib;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Macros with static information.
|
Macros with static information.
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package util;
|
package lib;
|
||||||
|
|
||||||
import kernel.peripherals.Redstone.BundleMask;
|
import kernel.peripherals.Redstone.BundleMask;
|
||||||
|
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
package util;
|
package lib;
|
||||||
|
|
||||||
|
import kernel.log.Log;
|
||||||
import lua.NativeStringTools;
|
import lua.NativeStringTools;
|
||||||
import lib.ui.Canvas;
|
import lib.ui.Canvas;
|
||||||
import cc.ComputerCraft;
|
import cc.ComputerCraft;
|
||||||
import kernel.Log;
|
|
||||||
#if webconsole
|
#if webconsole
|
||||||
import cc.HTTP;
|
import cc.HTTP;
|
||||||
import kernel.net.Net;
|
import kernel.net.Net;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package util;
|
package lib;
|
||||||
|
|
||||||
import haxe.Exception;
|
import haxe.Exception;
|
||||||
|
|
||||||
109
src/lib/HomeContext.hx
Normal file
109
src/lib/HomeContext.hx
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
package lib;
|
||||||
|
|
||||||
|
import lib.ui.elements.UIElement;
|
||||||
|
import lib.ui.elements.TextElement;
|
||||||
|
import lib.ui.elements.RootElement;
|
||||||
|
import kernel.KernelEvents;
|
||||||
|
import bin.Terminal;
|
||||||
|
import kernel.ui.WindowManager;
|
||||||
|
import kernel.ui.WindowContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
The WindowContext that spawns all other contexts. This is the main terminal.
|
||||||
|
Listens to global hotkey "POS1"/"HOME" to switch back to this context.
|
||||||
|
**/
|
||||||
|
class HomeContext {
|
||||||
|
private static inline final MAX_CONTEXT:Int = 10;
|
||||||
|
|
||||||
|
private var ctx:WindowContext = null;
|
||||||
|
private final workspaces:Map<Int, WindowContext> = [];
|
||||||
|
private var currentWorkspace:Int = -1;
|
||||||
|
|
||||||
|
private var renderer:RootElement;
|
||||||
|
|
||||||
|
public function new() {}
|
||||||
|
|
||||||
|
public function run() {
|
||||||
|
// Create main terminal context
|
||||||
|
ctx = WindowManager.instance.createNewContext();
|
||||||
|
WindowManager.instance.focusContextToOutput(ctx, "main");
|
||||||
|
|
||||||
|
renderer = new RootElement();
|
||||||
|
|
||||||
|
ctx.delegateEvents(renderer);
|
||||||
|
render();
|
||||||
|
|
||||||
|
// Register global key bindings to react to main terminal
|
||||||
|
KernelEvents.instance.onKey.handle(e -> {
|
||||||
|
// Is HOME pressed?
|
||||||
|
// TODO: remove magic number
|
||||||
|
if (e.keyCode == 268) {
|
||||||
|
focusMainTerm();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Adds a context to the list of contexts to manage. Returns the context ID. Returns -1 if the context could not be added.
|
||||||
|
**/
|
||||||
|
private function addContextNextWorkspace(context:WindowContext):Int {
|
||||||
|
for (i in 0...MAX_CONTEXT) {
|
||||||
|
if (!workspaces.exists(i)) {
|
||||||
|
workspaces[i] = context;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function addContextToWorkspace(context:WindowContext, id:Int) {
|
||||||
|
if (!workspaces.exists(id)) {
|
||||||
|
workspaces[id] = context;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function focusContext(id:Int) {
|
||||||
|
if (workspaces.exists(id)) {
|
||||||
|
WindowManager.instance.focusContextToOutput(workspaces[id], "main");
|
||||||
|
currentWorkspace = id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function focusMainTerm() {
|
||||||
|
render();
|
||||||
|
WindowManager.instance.focusContextToOutput(ctx, "main");
|
||||||
|
currentWorkspace = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function handleSelectContext(contextId:Int) {
|
||||||
|
if (workspaces[contextId] != null) {
|
||||||
|
focusContext(contextId);
|
||||||
|
} else {
|
||||||
|
var newContext = WindowManager.instance.createNewContext();
|
||||||
|
|
||||||
|
// Create new terminal
|
||||||
|
var term = new Terminal();
|
||||||
|
term.invoke(newContext);
|
||||||
|
|
||||||
|
addContextToWorkspace(newContext, contextId);
|
||||||
|
focusContext(contextId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function render() {
|
||||||
|
ctx.clear();
|
||||||
|
|
||||||
|
var list = [
|
||||||
|
for (i in 0...MAX_CONTEXT) workspaces.exists(i) ? 'Switch to context ${i}' : 'Create new Terminal on context ${i}'
|
||||||
|
];
|
||||||
|
|
||||||
|
var children:Array<UIElement> = [
|
||||||
|
for (i => line in list) new TextElement(line, {onClick: this.handleSelectContext.bind(i)})
|
||||||
|
];
|
||||||
|
|
||||||
|
renderer.setChildren(children);
|
||||||
|
|
||||||
|
renderer.render().renderToContext(ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package util;
|
package lib;
|
||||||
|
|
||||||
class MathI {
|
class MathI {
|
||||||
public static function max(a:Int, b:Int):Int {
|
public static function max(a:Int, b:Int):Int {
|
||||||
24
src/lib/ObjMerge.hx
Normal file
24
src/lib/ObjMerge.hx
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
package lib;
|
||||||
|
|
||||||
|
class ObjMerge {
|
||||||
|
public static function merge<T>(obj1:T, obj2:T): T {
|
||||||
|
if (obj1 == null) {
|
||||||
|
return obj2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj2 == null) {
|
||||||
|
return obj1;
|
||||||
|
}
|
||||||
|
|
||||||
|
var rtn:T = Reflect.copy(obj1);
|
||||||
|
var fields = Reflect.fields(obj2);
|
||||||
|
|
||||||
|
for (field in fields) {
|
||||||
|
if (Reflect.getProperty(obj1, field) == null) {
|
||||||
|
Reflect.setProperty(rtn, field, Reflect.getProperty(obj2, field));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (rtn:T);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package util;
|
package lib;
|
||||||
|
|
||||||
import util.Vec.Vec2;
|
import lib.Vec.Vec2;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Reporesents a Point in a 2D `Int` System.
|
Reporesents a Point in a 2D `Int` System.
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package util;
|
package lib;
|
||||||
|
|
||||||
import util.Vec.Vec3;
|
import lib.Vec.Vec3;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Reporesents a Point in a 3D `Int` System.
|
Reporesents a Point in a 3D `Int` System.
|
||||||
46
src/lib/Rect.hx
Normal file
46
src/lib/Rect.hx
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
package lib;
|
||||||
|
|
||||||
|
class Rect {
|
||||||
|
|
||||||
|
private final tl:Pos;
|
||||||
|
private final br:Pos;
|
||||||
|
|
||||||
|
public function new(p1: Pos,p2:Pos) {
|
||||||
|
this.tl = {
|
||||||
|
x: MathI.min(p1.x,p2.x),
|
||||||
|
y: MathI.min(p1.y,p2.y)
|
||||||
|
};
|
||||||
|
|
||||||
|
this.br = {
|
||||||
|
x: MathI.max(p1.x,p2.x),
|
||||||
|
y: MathI.max(p1.y,p2.y)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSize(): Int {
|
||||||
|
return getWidth() * getHight();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isInside(p: Pos): Bool {
|
||||||
|
return (p.x >= tl.x && p.x <= br.x) && (p.y >= tl.y && p.y <= br.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isOutside(p: Pos): Bool {
|
||||||
|
return !this.isInside(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getHight(): Int {
|
||||||
|
return br.y - tl.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getWidth(): Int {
|
||||||
|
return br.x - tl.x;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function offset(pos: Pos) {
|
||||||
|
tl.x += pos.x;
|
||||||
|
tl.y += pos.y;
|
||||||
|
br.x += pos.x;
|
||||||
|
br.y += pos.y;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package lib;
|
package lib;
|
||||||
|
|
||||||
import util.Color;
|
import lib.Color;
|
||||||
import kernel.ui.TermWriteable;
|
import kernel.ui.TermWriteable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
12
src/lib/Vec.hx
Normal file
12
src/lib/Vec.hx
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package lib;
|
||||||
|
|
||||||
|
@:structInit class Vec2<T> {
|
||||||
|
public var x:T;
|
||||||
|
public var y:T;
|
||||||
|
}
|
||||||
|
|
||||||
|
@:structInit class Vec3<T> {
|
||||||
|
public var x:T;
|
||||||
|
public var y:T;
|
||||||
|
public var z:T;
|
||||||
|
}
|
||||||
7
src/lib/cli/CLIApp.hx
Normal file
7
src/lib/cli/CLIApp.hx
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package lib.cli;
|
||||||
|
|
||||||
|
using tink.CoreApi;
|
||||||
|
|
||||||
|
abstract class CLIApp {
|
||||||
|
public abstract function invoke(handle: TermHandle): Future<Bool>;
|
||||||
|
}
|
||||||
29
src/lib/cli/TermHandle.hx
Normal file
29
src/lib/cli/TermHandle.hx
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
package lib.cli;
|
||||||
|
|
||||||
|
import haxe.ds.ReadOnlyArray;
|
||||||
|
|
||||||
|
typedef TermHandleEvents = {
|
||||||
|
onWrite: String->Void,
|
||||||
|
onNewLine: Void->Void,
|
||||||
|
}
|
||||||
|
|
||||||
|
class TermHandle {
|
||||||
|
public final args:ReadOnlyArray<String>;
|
||||||
|
private final events:TermHandleEvents;
|
||||||
|
|
||||||
|
@:allow(bin.Terminal)
|
||||||
|
private function new(args: Array<String>, events: TermHandleEvents) {
|
||||||
|
this.args = args;
|
||||||
|
this.events = events;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function write(s:String) {
|
||||||
|
this.events.onWrite(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function writeLn(s:String = "") {
|
||||||
|
if (s != "")
|
||||||
|
this.events.onWrite(s);
|
||||||
|
this.events.onNewLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
27
src/lib/observable/DummyObservable.hx
Normal file
27
src/lib/observable/DummyObservable.hx
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package lib.observable;
|
||||||
|
|
||||||
|
using tink.CoreApi;
|
||||||
|
|
||||||
|
class DummyObservable<T> implements Observable<T> {
|
||||||
|
private var value:T;
|
||||||
|
|
||||||
|
private function new(value:T) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function set(value:T) {
|
||||||
|
throw new haxe.exceptions.NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get():T {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function subscribe(callback:Callback<T>):CallbackLink {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function dummy<T>(value: T): Observable<T> {
|
||||||
|
return new DummyObservable<T>(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
9
src/lib/observable/Observable.hx
Normal file
9
src/lib/observable/Observable.hx
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package lib.observable;
|
||||||
|
|
||||||
|
using tink.CoreApi;
|
||||||
|
|
||||||
|
interface Observable<T> {
|
||||||
|
public function set(value:T): Void;
|
||||||
|
public function get():T;
|
||||||
|
public function subscribe(callback:Callback<T>):CallbackLink;
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package util;
|
package lib.observable;
|
||||||
|
|
||||||
class ObservableArray<T> extends Observable<Array<T>> {
|
class ObservableArray<T> extends ObservableValue<Array<T>> {
|
||||||
public function new(value: Array<T>) {
|
public function new(value: Array<T>) {
|
||||||
super(value);
|
super(value);
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
package util;
|
package lib.observable;
|
||||||
|
|
||||||
using tink.CoreApi;
|
using tink.CoreApi;
|
||||||
|
|
||||||
class Observable<T> {
|
class ObservableValue<T> implements Observable<T> {
|
||||||
private var value:T;
|
private var value:T;
|
||||||
private var callbacks:CallbackList<T> = new CallbackList();
|
private var callbacks:CallbackList<T> = new CallbackList();
|
||||||
|
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
package lib.turtle;
|
package lib.turtle;
|
||||||
|
|
||||||
import util.Pos;
|
import lib.Pos;
|
||||||
import util.Pos3;
|
import lib.Pos3;
|
||||||
import kernel.turtle.Turtle;
|
import kernel.turtle.Turtle;
|
||||||
|
|
||||||
using tink.CoreApi;
|
using tink.CoreApi;
|
||||||
|
|
||||||
class TurtleExecuter {
|
class TurtleExecuter {
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import lua.NativeStringTools;
|
|||||||
|
|
||||||
using tink.CoreApi;
|
using tink.CoreApi;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Save a set of turtle instructions to a string and execute them.
|
Save a set of turtle instructions to a string and execute them.
|
||||||
**/
|
**/
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
package lib.ui;
|
package lib.ui;
|
||||||
|
|
||||||
import util.Pos;
|
import kernel.ui.WindowContext;
|
||||||
import util.Rect;
|
import lib.Pos;
|
||||||
|
import lib.Rect;
|
||||||
import kernel.ui.Pixel;
|
import kernel.ui.Pixel;
|
||||||
|
|
||||||
abstract Canvas(Array<Array<Pixel>>) to Array<Array<Pixel>> from Array<Array<Pixel>>{
|
abstract Canvas(Array<Array<Pixel>>) to Array<Array<Pixel>> from Array<Array<Pixel>>{
|
||||||
@@ -63,10 +64,65 @@ abstract Canvas(Array<Array<Pixel>>) to Array<Array<Pixel>> from Array<Array<Pix
|
|||||||
|
|
||||||
public function getBounds(): Rect {
|
public function getBounds(): Rect {
|
||||||
return new Rect({x:0,y:0},{
|
return new Rect({x:0,y:0},{
|
||||||
x: maxWidth(),
|
x: maxWidth() - 1,
|
||||||
y: height()
|
y: height() - 1
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Renders the canvas directly to the context
|
||||||
|
**/
|
||||||
|
public function renderToContext(ctx: WindowContext){
|
||||||
|
var lastBgColor: Color = null;
|
||||||
|
var lastTextColor: Color = null;
|
||||||
|
|
||||||
|
for (lineIndex => line in this) {
|
||||||
|
if (line == null || line.length == 0) {
|
||||||
|
// Line is empty
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.setCursorPos(0, lineIndex);
|
||||||
|
|
||||||
|
var pritableLine = "";
|
||||||
|
|
||||||
|
for (pixelIndex => pixel in line) {
|
||||||
|
if (pixel == null) {
|
||||||
|
// If the pixel is null we need to skip it with setCurosrPos.
|
||||||
|
// Otherwise we will print an empty space with bg color
|
||||||
|
ctx.write(pritableLine);
|
||||||
|
pritableLine = "";
|
||||||
|
ctx.setCursorPos(pixelIndex + 1, lineIndex);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pixel.bg != lastBgColor) {
|
||||||
|
// The background color has changed, we need to print the current line and set the new background color
|
||||||
|
ctx.write(pritableLine);
|
||||||
|
pritableLine = "";
|
||||||
|
|
||||||
|
// Set the new background color
|
||||||
|
ctx.setBackgroundColor(pixel.bg);
|
||||||
|
lastBgColor = pixel.bg;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Same as above but for the text color
|
||||||
|
if (pixel.textColor != lastTextColor) {
|
||||||
|
// The text color has changed, we need to print the current line and set the new text color
|
||||||
|
ctx.write(pritableLine);
|
||||||
|
pritableLine = "";
|
||||||
|
|
||||||
|
// Set the new text color
|
||||||
|
ctx.setTextColor(pixel.textColor);
|
||||||
|
lastTextColor = pixel.textColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
pritableLine += pixel.char;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.write(pritableLine);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class CanvasKeyValueIterator {
|
class CanvasKeyValueIterator {
|
||||||
|
|||||||
8
src/lib/ui/Dimensions.hx
Normal file
8
src/lib/ui/Dimensions.hx
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
package lib.ui;
|
||||||
|
|
||||||
|
typedef Dimensions = {
|
||||||
|
public var ?top:Int;
|
||||||
|
public var ?bottom:Int;
|
||||||
|
public var ?left:Int;
|
||||||
|
public var ?right:Int;
|
||||||
|
}
|
||||||
9
src/lib/ui/UIApp.hx
Normal file
9
src/lib/ui/UIApp.hx
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package lib.ui;
|
||||||
|
|
||||||
|
import kernel.ui.WindowContext;
|
||||||
|
|
||||||
|
using tink.CoreApi;
|
||||||
|
|
||||||
|
abstract class UIApp {
|
||||||
|
public abstract function invoke(context: WindowContext): Future<Bool>;
|
||||||
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
package lib.ui;
|
package lib.ui;
|
||||||
|
|
||||||
import util.Pos;
|
import lib.Pos;
|
||||||
import util.Vec.Vec2;
|
|
||||||
import kernel.ButtonType;
|
import kernel.ButtonType;
|
||||||
|
|
||||||
using tink.CoreApi;
|
using tink.CoreApi;
|
||||||
|
|
||||||
typedef UIEvents = {
|
typedef UIEvents = {
|
||||||
@@ -13,4 +13,5 @@ typedef UIEvents = {
|
|||||||
public var ?onMouseScroll:Callback<{dir:Int, pos:Pos}>;
|
public var ?onMouseScroll:Callback<{dir:Int, pos:Pos}>;
|
||||||
public var ?onMouseUp:Callback<{button:ButtonType, pos:Pos}>;
|
public var ?onMouseUp:Callback<{button:ButtonType, pos:Pos}>;
|
||||||
public var ?onPaste:Callback<String>;
|
public var ?onPaste:Callback<String>;
|
||||||
|
public var ?onChar:Callback<String>;
|
||||||
}
|
}
|
||||||
|
|||||||
26
src/lib/ui/elements/EventMap.hx
Normal file
26
src/lib/ui/elements/EventMap.hx
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
package lib.ui.elements;
|
||||||
|
|
||||||
|
import lib.ui.rendere.UIEventDelegate;
|
||||||
|
|
||||||
|
abstract EventMap(List<{bound:Rect, delegate:UIEventDelegate}>) {
|
||||||
|
inline public function new(?i:List<{bound:Rect, delegate:UIEventDelegate}>) {
|
||||||
|
if (i == null) {
|
||||||
|
this = new List<{bound:Rect, delegate:UIEventDelegate}>();
|
||||||
|
} else {
|
||||||
|
this = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findResponseableDelegate(pos:Pos):UIEventDelegate {
|
||||||
|
for (i in this) {
|
||||||
|
if (i.bound.isInside(pos)) {
|
||||||
|
return i.delegate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addElement(element:UIEventDelegate, bound:Rect) {
|
||||||
|
this.add({bound: bound, delegate: element});
|
||||||
|
}
|
||||||
|
}
|
||||||
42
src/lib/ui/elements/RootElement.hx
Normal file
42
src/lib/ui/elements/RootElement.hx
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
package lib.ui.elements;
|
||||||
|
|
||||||
|
class RootElement implements UIElement {
|
||||||
|
private var children:Array<UIElement>;
|
||||||
|
private final eventManager:UIEventManager = new UIEventManager();
|
||||||
|
|
||||||
|
public function new(?children:Array<UIElement>) {
|
||||||
|
if (children == null) {
|
||||||
|
children = [];
|
||||||
|
} else {
|
||||||
|
this.children = children;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getEventHandlers():UIEvents {
|
||||||
|
return eventManager.getEventHandlers();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setChildren(children:Array<UIElement>) {
|
||||||
|
this.children = children;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render():Canvas {
|
||||||
|
var canvas = new Canvas();
|
||||||
|
var offset = new Pos({x: 0, y: 0});
|
||||||
|
|
||||||
|
this.eventManager.clearMap();
|
||||||
|
|
||||||
|
for (child in children) {
|
||||||
|
var childCanvas = child.render();
|
||||||
|
var bounds = childCanvas.getBounds();
|
||||||
|
bounds.offset(offset);
|
||||||
|
|
||||||
|
this.eventManager.addMapElement(child, bounds);
|
||||||
|
canvas.combine(childCanvas, offset);
|
||||||
|
|
||||||
|
offset = new Pos({x: 0, y: offset.y + bounds.getHight() + 1});
|
||||||
|
}
|
||||||
|
|
||||||
|
return canvas;
|
||||||
|
}
|
||||||
|
}
|
||||||
36
src/lib/ui/elements/TextElement.hx
Normal file
36
src/lib/ui/elements/TextElement.hx
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
package lib.ui.elements;
|
||||||
|
|
||||||
|
import lib.ui.elements.UIElement;
|
||||||
|
|
||||||
|
class TextElement implements UIElement {
|
||||||
|
public var text:String;
|
||||||
|
|
||||||
|
private final uiEvents:UIEvents;
|
||||||
|
|
||||||
|
public function new(text:String, ?uiEvents:UIEvents) {
|
||||||
|
this.text = text;
|
||||||
|
this.uiEvents = uiEvents;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function set(text:String) {
|
||||||
|
this.text = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get():String {
|
||||||
|
return this.text;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getEventHandlers():UIEvents {
|
||||||
|
return uiEvents;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render():Canvas {
|
||||||
|
var canvas = new Canvas();
|
||||||
|
for (i in 0...this.text.length) {
|
||||||
|
var c = this.text.charAt(i);
|
||||||
|
canvas.set({x: i, y: 0}, {bg: Black, textColor: White, char: c});
|
||||||
|
}
|
||||||
|
|
||||||
|
return canvas;
|
||||||
|
}
|
||||||
|
}
|
||||||
7
src/lib/ui/elements/UIElement.hx
Normal file
7
src/lib/ui/elements/UIElement.hx
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package lib.ui.elements;
|
||||||
|
|
||||||
|
import lib.ui.rendere.UIEventDelegate;
|
||||||
|
|
||||||
|
interface UIElement extends UIEventDelegate {
|
||||||
|
public function render():Canvas;
|
||||||
|
}
|
||||||
60
src/lib/ui/elements/UIEventManager.hx
Normal file
60
src/lib/ui/elements/UIEventManager.hx
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
package lib.ui.elements;
|
||||||
|
|
||||||
|
import kernel.log.Log;
|
||||||
|
import kernel.ButtonType;
|
||||||
|
import lib.ui.rendere.UIEventDelegate;
|
||||||
|
|
||||||
|
class UIEventManager implements UIEventDelegate {
|
||||||
|
private var map:EventMap = new EventMap();
|
||||||
|
|
||||||
|
public function new() {}
|
||||||
|
|
||||||
|
public function clearMap() {
|
||||||
|
this.map = new EventMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addMapElement(element:UIEventDelegate, bound:Rect) {
|
||||||
|
this.map.addElement(element, bound);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getEventHandlers():UIEvents {
|
||||||
|
return {
|
||||||
|
onClick: handleClickEvent,
|
||||||
|
onKey: handleKeyEvent,
|
||||||
|
onKeyUp: handleKeyUpEvent,
|
||||||
|
onMouseDrag: handleMouseDragEvent,
|
||||||
|
onMouseScroll: handleMouseScrollEvent,
|
||||||
|
onMouseUp: handleMouseUpEvent,
|
||||||
|
onPaste: handlePasteEvent,
|
||||||
|
onChar: handleCharEvent,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private function handleClickEvent(params:{button:ButtonType, pos:Pos}) {
|
||||||
|
var element = this.map.findResponseableDelegate(params.pos);
|
||||||
|
if (element == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var handlers = element.getEventHandlers();
|
||||||
|
if (handlers == null || handlers.onClick == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
handlers.onClick.invoke(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function handleCharEvent(char:String) {}
|
||||||
|
|
||||||
|
private function handlePasteEvent(text:String) {}
|
||||||
|
|
||||||
|
private function handleMouseUpEvent(params:{button:ButtonType, pos:Pos}) {}
|
||||||
|
|
||||||
|
private function handleMouseScrollEvent(params:{dir:Int, pos:Pos}) {}
|
||||||
|
|
||||||
|
private function handleMouseDragEvent(params:{button:ButtonType, pos:Pos}) {}
|
||||||
|
|
||||||
|
private function handleKeyEvent(params:{keyCode:Int, isHeld:Bool}) {}
|
||||||
|
|
||||||
|
private function handleKeyUpEvent(key:Int) {}
|
||||||
|
}
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
package lib.ui.reactive;
|
package lib.ui.reactive;
|
||||||
|
|
||||||
import util.Pos;
|
import lib.Pos;
|
||||||
import util.Rect;
|
import util.Rect;
|
||||||
import util.ObservableArray;
|
import util.ObservableArray;
|
||||||
import util.Vec.Vec2;
|
import lib.Vec.Vec2;
|
||||||
|
|
||||||
class ListElement extends UIElement {
|
class ListElement extends UIElement {
|
||||||
|
|
||||||
|
|||||||
@@ -1,40 +1,77 @@
|
|||||||
package lib.ui.reactive;
|
package lib.ui.reactive;
|
||||||
|
|
||||||
import util.Pos;
|
import lib.ui.Dimensions;
|
||||||
|
import util.ObjMerge;
|
||||||
|
import lib.Pos;
|
||||||
import util.Observable;
|
import util.Observable;
|
||||||
|
import lib.Color;
|
||||||
|
import lib.Vec.Vec2;
|
||||||
|
|
||||||
using tink.CoreApi;
|
using tink.CoreApi;
|
||||||
|
|
||||||
import util.Color;
|
typedef TextElementArgs = {
|
||||||
import util.Vec.Vec2;
|
public var ?text:Observable<String>;
|
||||||
import util.MathI;
|
public var ?simpleText: String;
|
||||||
|
public var ?bg:Color;
|
||||||
|
public var ?fg:Color;
|
||||||
|
public var ?padding: Dimensions;
|
||||||
|
public var ?margin: Dimensions;
|
||||||
|
}
|
||||||
|
|
||||||
class TextElement extends UIElement {
|
class TextElement extends UIElement {
|
||||||
private final text:Observable<String>;
|
private var args:TextElementArgs;
|
||||||
private final bg:Color;
|
|
||||||
private final fg:Color;
|
|
||||||
|
|
||||||
public function new(text:Observable<String>, ?background:Color = Black, ?textColor:Color = White, events: UIEvents = null) {
|
public function new(args: TextElementArgs,events: UIEvents = null) {
|
||||||
super(events);
|
super(events);
|
||||||
|
this.args = args;
|
||||||
|
|
||||||
this.text = text;
|
if (args.text != null) {
|
||||||
this.bg = background;
|
args.text.subscribe(value -> {
|
||||||
this.fg = textColor;
|
|
||||||
|
|
||||||
this.text.subscribe(value -> {
|
|
||||||
this.changedTrigger.trigger(null);
|
this.changedTrigger.trigger(null);
|
||||||
});
|
});
|
||||||
|
}else if (args.simpleText == null) {
|
||||||
|
throw new Error("text or simpleText must be set");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getText() {
|
||||||
|
if (args.text != null) {
|
||||||
|
return args.text.get();
|
||||||
|
} else {
|
||||||
|
return args.simpleText;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render(bounds:Vec2<Int>,offset: Pos):Canvas {
|
public function render(bounds:Vec2<Int>,offset: Pos):Canvas {
|
||||||
var rtn = new Canvas();
|
var rtn = new Canvas();
|
||||||
|
|
||||||
for (i in 0...MathI.min(Math.floor(text.get().length / bounds.x) + 1, bounds.y)) {
|
var fullText = getText();
|
||||||
var line = (text.get().substr(i * bounds.x, bounds.x));
|
|
||||||
for (char in 0...line.length) {
|
var writePoint: Pos = {x: 0,y: 0};
|
||||||
rtn.set({x: char, y: i}, {textColor: fg, char: line.charAt(char), bg: bg});
|
|
||||||
}
|
for (pos in 0...fullText.length) {
|
||||||
|
var char = fullText.charAt(pos);
|
||||||
|
|
||||||
|
if (char == "\n"){
|
||||||
|
writePoint = {x: 0, y: writePoint.y + 1};
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
return rtn;
|
if (writePoint.y >= bounds.y) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (writePoint.x >= bounds.x) {
|
||||||
|
writePoint = {x: 0, y: writePoint.y + 1};
|
||||||
|
}
|
||||||
|
|
||||||
|
rtn.set(writePoint, {char: char,textColor: this.args.fg, bg: this.args.bg});
|
||||||
|
|
||||||
|
writePoint = {x: writePoint.x + 1, y: writePoint.y};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return UIElement.applyPaddignAndMargin(rtn, this.args.padding, this.args.margin);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
60
src/lib/ui/reactive/TurtleController.hx
Normal file
60
src/lib/ui/reactive/TurtleController.hx
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
package lib.ui.reactive;
|
||||||
|
|
||||||
|
import lib.Debug;
|
||||||
|
import lib.Vec.Vec2;
|
||||||
|
import lib.Pos;
|
||||||
|
|
||||||
|
class TurtleController extends UIElement {
|
||||||
|
|
||||||
|
public function new() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function addButton(label:String): Canvas {
|
||||||
|
var txt = new TextElement({simpleText: '$label', fg: Red,bg: Orange});
|
||||||
|
return txt.render({x:3,y:3},{x:0,y:0});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render(bounds:Vec2<Int>, offset:Pos):Canvas {
|
||||||
|
var canvas: Canvas = new Canvas();
|
||||||
|
|
||||||
|
canvas.combine(addButton("F"),{x:1,y:1});
|
||||||
|
canvas.combine(addButton("F"),{x:5,y:2});
|
||||||
|
|
||||||
|
return canvas;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// var innerText = switch (sqr) {
|
||||||
|
// case 0: "D";
|
||||||
|
// case 1: "F";
|
||||||
|
// case 2: "U";
|
||||||
|
// case 3: "L";
|
||||||
|
// case 4: "B";
|
||||||
|
// case 5: "R";
|
||||||
|
// case 6: "D";
|
||||||
|
// case 7: "D";
|
||||||
|
// case 8: "D";
|
||||||
|
// default: "?";
|
||||||
|
// };
|
||||||
|
|
||||||
|
// canvas.set({x: offsetX + 0, y: offsetY + 0}, {char: borderChar,bg: bgColor,textColor: textColor});
|
||||||
|
// canvas.set({x: offsetX + 1, y: offsetY + 0}, {char: borderChar,bg: bgColor,textColor: textColor});
|
||||||
|
// canvas.set({x: offsetX + 2, y: offsetY + 0}, {char: borderChar,bg: bgColor,textColor: textColor});
|
||||||
|
// canvas.set({x: offsetX + 3, y: offsetY + 0}, {char: " ",bg: spaceColor,textColor: textColor});
|
||||||
|
|
||||||
|
// canvas.set({x: offsetX + 0, y: offsetY + 1}, {char: borderChar,bg: bgColor,textColor: textColor});
|
||||||
|
// canvas.set({x: offsetX + 1, y: offsetY + 1}, {char: innerText,bg: bgColor,textColor: textColor});
|
||||||
|
// canvas.set({x: offsetX + 2, y: offsetY + 1}, {char: borderChar,bg: bgColor,textColor: textColor});
|
||||||
|
// canvas.set({x: offsetX + 3, y: offsetY + 1}, {char: " ",bg: spaceColor,textColor: textColor});
|
||||||
|
|
||||||
|
// canvas.set({x: offsetX + 0, y: offsetY + 2}, {char: borderChar,bg: bgColor,textColor: textColor});
|
||||||
|
// canvas.set({x: offsetX + 1, y: offsetY + 2}, {char: borderChar,bg: bgColor,textColor: textColor});
|
||||||
|
// canvas.set({x: offsetX + 2, y: offsetY + 2}, {char: borderChar,bg: bgColor,textColor: textColor});
|
||||||
|
// canvas.set({x: offsetX + 3, y: offsetY + 2}, {char: " ",bg: spaceColor,textColor: textColor});
|
||||||
|
|
||||||
|
// canvas.set({x: offsetX + 0, y: offsetY + 3}, {char: " ",bg: spaceColor,textColor: textColor});
|
||||||
|
// canvas.set({x: offsetX + 1, y: offsetY + 3}, {char: " ",bg: spaceColor,textColor: textColor});
|
||||||
|
// canvas.set({x: offsetX + 2, y: offsetY + 3}, {char: " ",bg: spaceColor,textColor: textColor});
|
||||||
|
// canvas.set({x: offsetX + 3, y: offsetY + 3}, {char: " ",bg: spaceColor,textColor: textColor});
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
package lib.ui.reactive;
|
package lib.ui.reactive;
|
||||||
|
|
||||||
import util.Pos;
|
import util.ObjMerge;
|
||||||
import util.Rect;
|
import lib.Pos;
|
||||||
import util.Vec.Vec2;
|
import lib.Rect;
|
||||||
|
import lib.Vec.Vec2;
|
||||||
|
|
||||||
using tink.CoreApi;
|
using tink.CoreApi;
|
||||||
|
|
||||||
@@ -33,4 +34,14 @@ abstract class UIElement {
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public static function applyPaddignAndMargin(canvas:Canvas,padding: Dimensions, margin: Dimensions):Canvas{
|
||||||
|
var passing = ObjMerge.merge(padding, {top: 0, left: 0, bottom: 0, right: 0});
|
||||||
|
var margin = ObjMerge.merge(margin, {top: 0, left: 0, bottom: 0, right: 0});
|
||||||
|
var rtn = new Canvas();
|
||||||
|
|
||||||
|
rtn.combine(canvas,{x:margin.left + padding.left,y: margin.top + padding.top});
|
||||||
|
|
||||||
|
return rtn;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
38
src/lib/ui/rendere/List.hx
Normal file
38
src/lib/ui/rendere/List.hx
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
package lib.ui.rendere;
|
||||||
|
|
||||||
|
class List implements UIEventDelegate{
|
||||||
|
private final onElementClick: Null<Int->Void>;
|
||||||
|
|
||||||
|
public function new(?onElementClick: Int->Void) {
|
||||||
|
this.onElementClick = onElementClick;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render(list:Array<String>): Canvas {
|
||||||
|
var canvas = new Canvas();
|
||||||
|
for (line in 0...list.length) {
|
||||||
|
for (char in 0...list[line].length) {
|
||||||
|
var c = list[line].charAt(char);
|
||||||
|
canvas.set({x: char, y: line}, {
|
||||||
|
char: c,
|
||||||
|
textColor: Color.White,
|
||||||
|
bg: Color.Black,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return canvas;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getEventHandlers(): UIEvents{
|
||||||
|
return {
|
||||||
|
onClick: handleClick
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private function handleClick(e: {button:kernel.ButtonType, pos:Pos}): Void{
|
||||||
|
if (this.onElementClick == null){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.onElementClick(e.pos.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
5
src/lib/ui/rendere/UIEventDelegate.hx
Normal file
5
src/lib/ui/rendere/UIEventDelegate.hx
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
package lib.ui.rendere;
|
||||||
|
|
||||||
|
interface UIEventDelegate {
|
||||||
|
public function getEventHandlers(): UIEvents;
|
||||||
|
}
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
package util;
|
|
||||||
|
|
||||||
import util.Vec.Vec2;
|
|
||||||
|
|
||||||
class Rect {
|
|
||||||
|
|
||||||
private final tr:Pos;
|
|
||||||
private final bl:Pos;
|
|
||||||
|
|
||||||
public function new(p1: Pos,p2:Pos) {
|
|
||||||
this.tr = {
|
|
||||||
x: MathI.max(p1.x,p2.x),
|
|
||||||
y: MathI.max(p1.y,p2.y)
|
|
||||||
};
|
|
||||||
|
|
||||||
this.bl = {
|
|
||||||
x: MathI.min(p1.x,p2.x),
|
|
||||||
y: MathI.min(p1.y,p2.y)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getSize(): Int {
|
|
||||||
return (tr.x - bl.x) * (tr.y - bl.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function isInside(p: Pos): Bool {
|
|
||||||
return (p.x >= bl.x && p.x <= tr.x) && (p.y >= bl.y && p.y <= tr.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function isOutside(p: Pos): Bool {
|
|
||||||
return !this.isInside(p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
package util;
|
|
||||||
|
|
||||||
@:structInit class Vec2<T> {
|
|
||||||
public final x:T;
|
|
||||||
public final y:T;
|
|
||||||
}
|
|
||||||
|
|
||||||
@:structInit class Vec3<T> {
|
|
||||||
public final x:T;
|
|
||||||
public final y:T;
|
|
||||||
public final z:T;
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user