diff --git a/src/kernel/ui/BufferedVirtualTermWriter.hx b/src/kernel/ui/BufferedVirtualTermWriter.hx new file mode 100644 index 0000000..1c33796 --- /dev/null +++ b/src/kernel/ui/BufferedVirtualTermWriter.hx @@ -0,0 +1,180 @@ +package kernel.ui; + +import lib.Pos; +import lib.Vec.Vec2; +import lib.Color; +import kernel.ui.TermWriteable; + +using tink.CoreApi; + +/** + A term writer that can switch beetween its internal buffer and another termwriter. + The other target is most of the time a real screen +**/ +class BufferedVirtualTermWriter implements VirtualTermWriter extends TermBuffer { + private static final defaultSize:Vec2 = {x: 50, y: 50}; + + private var target:TermWriteable; + private var enabled:Bool = false; + private var onResizeLink:CallbackLink; + + @:allow(kernel.ui) + private function new(?target:TermWriteable) { + setTarget(target); + + if (enabled) { + enable(); + } + + super(); + } + + public function enable() { + if (target != null) { + enabled = true; + super.copyBufferToTarget(target); + } + } + + public function disable() { + enabled = false; + } + + public inline function isEnabled():Bool { + return enabled; + } + + public function setTarget(newTarget:TermWriteable) { + if (newTarget != null) { + super.setSize(newTarget.getSize()); + + // Remove old target event listner + if (onResizeLink != null) { + onResizeLink.cancel(); + } + + // Add new target event listner + onResizeLink = newTarget.onResize.handle(newSize -> { + setSuperSize(newSize); + }); + + newTarget.reset(); + + target = newTarget; + } + } + + private function setSuperSize(size:Vec2) { + super.setSize(target.getSize()); + } + + // + // TermWriteable functions. + // + public override function write(text:String) { + if (isEnabled()) { + target.write(text); + } + + super.write(text); + } + + public override function scroll(y:Int) { + if (isEnabled()) { + target.scroll(y); + } + super.scroll(y); + } + + public override function getCursorPos():Pos { + if (isEnabled()) { + return target.getCursorPos(); + } else { + return super.getCursorPos(); + } + } + + public override function setCursorPos(x:Int, y:Int) { + if (isEnabled()) { + target.setCursorPos(x, y); + } + + super.setCursorPos(x, y); + } + + public override function getCursorBlink():Bool { + if (isEnabled()){ + return target.getCursorBlink(); + }else{ + return super.getCursorBlink(); + } + } + + public override function setCursorBlink(blink:Bool) { + if (isEnabled()){ + target.setCursorBlink(blink); + } + super.setCursorBlink(blink); + } + + public override function getSize():Vec2 { + // TODO: make sense ? + if (target != null) { + return target.getSize(); + } + + return defaultSize; + } + + public override function clear() { + if (isEnabled()) { + target.clear(); + } + + super.clear(); + } + + public override function clearLine() { + if (isEnabled()) { + target.clearLine(); + } + + super.clearLine(); + } + + public override function getTextColor():Color { + if (isEnabled()) { + return target.getTextColor(); + } + + return super.getTextColor(); + } + + public override function setTextColor(color:Color) { + if (isEnabled()) { + target.setTextColor(color); + } + + super.setTextColor(color); + } + + public override function getBackgroundColor():Color { + if (isEnabled()) { + return target.getBackgroundColor(); + } + + return super.getBackgroundColor(); + } + + public override function setBackgroundColor(color:Color) { + if (isEnabled()) { + target.setBackgroundColor(color); + } + + super.setBackgroundColor(color); + } + + public override function isColor():Bool { + throw new haxe.exceptions.NotImplementedException(); + } +} diff --git a/src/kernel/ui/StatelessVirtualTermWriter.hx b/src/kernel/ui/StatelessVirtualTermWriter.hx new file mode 100644 index 0000000..84cdc7f --- /dev/null +++ b/src/kernel/ui/StatelessVirtualTermWriter.hx @@ -0,0 +1,159 @@ +package kernel.ui; + +import kernel.log.Log; +import lib.Pos; +import lib.Vec.Vec2; +import lib.Color; + +using tink.CoreApi; + +/** + Normal TermWriteable but without storing the the state in a buffer wehen disabled. + When enabled or the screen size changes the render function is called. + The render function is only called when needed. + You can also request a re-render by calling `requestRender`. +**/ +class StatelessVirtualTermWriter implements VirtualTermWriter { + public var onResize(default, null):Signal>; + + private var onResizeTrigger: SignalTrigger> = Signal.trigger(); + private var target:TermWriteable; + private var enabled:Bool = false; + private var renderFunc:NullVoid> = null; + private var renderRequested:Bool = false; + private var onResizeLink: CallbackLink; + + @:allow(kernel.ui) + private function new(?target:TermWriteable) { + onResize = onResizeTrigger.asSignal(); + setTarget(target); + } + + public function setRenderFunc(func: (Void->Void)) { + this.renderFunc = func; + } + + public function requestRender() { + if (renderFunc == null) { + Log.warn("Render requested, but no render function set"); + return; + } + + if (enabled) { + renderFunc(); + renderRequested = false; + }else{ + renderRequested = true; + } + } + + // + // VirtualTermWriter implementation + // + + public function enable() { + if (target == null) { + return; + } + + enabled = true; + + if (renderFunc != null){ + renderFunc(); + } + + renderRequested = false; + } + + public function disable() { + enabled = false; + } + + public function setTarget(newTarget:TermWriteable) { + if (newTarget == null) { + return; + } + + if (target != null) { + onResizeLink.cancel(); + } + + onResizeLink = newTarget.onResize.handle((size) -> { + requestRender(); + onResizeTrigger.trigger(size); + }); + + target = newTarget; + } + + public function isEnabled():Bool { + return enabled; + } + + // + // TermWriteable implementation + // + // Some functions return defualt values if the target is not set. This should not be an issue since + // the render func is only called when the target is set. + // + + public inline function write(text:String) { + if (enabled) target.write(text); + } + + public inline function scroll(y:Int) { + if (enabled) target.scroll(y); + } + + public inline function getCursorPos():Pos { + return enabled ? target.getCursorPos() : new Pos({x: 0, y: 0}); + } + + public inline function setCursorPos(x:Int, y:Int) { + if (enabled) target.setCursorPos(x, y); + } + + public inline function getCursorBlink():Bool { + return enabled ? target.getCursorBlink() : false; + } + + public inline function setCursorBlink(blink:Bool) { + if (enabled) target.setCursorBlink(blink); + } + + public inline function getSize():Vec2 { + return enabled ? target.getSize() : {x: 0, y: 0}; + } + + public inline function clear() { + if (enabled) target.clear(); + } + + public inline function clearLine() { + if (enabled) target.clearLine(); + } + + public inline function getTextColor():Color { + return enabled ? target.getTextColor() : Color.White; + } + + public inline function setTextColor(color:Color) { + if (enabled) target.setTextColor(color); + } + + public inline function getBackgroundColor():Color { + return enabled ? target.getBackgroundColor() : Color.Black; + } + + public inline function setBackgroundColor(color:Color) { + if (enabled) target.setBackgroundColor(color); + } + + public inline function isColor():Bool { + return enabled ? target.isColor() : false; + } + + public inline function reset() { + if (enabled) target.reset(); + } +} diff --git a/src/kernel/ui/VirtualTermWriter.hx b/src/kernel/ui/VirtualTermWriter.hx index 24ee6ef..ed4387f 100644 --- a/src/kernel/ui/VirtualTermWriter.hx +++ b/src/kernel/ui/VirtualTermWriter.hx @@ -1,180 +1,12 @@ package kernel.ui; -import lib.Pos; -import lib.Vec.Vec2; -import lib.Color; -import kernel.ui.TermWriteable; - -using tink.CoreApi; - /** - A term writer that can switch beetween its internal buffer and another termwriter. - The other target is most of the time a real screen + A VirtualTermWriter is a TermWriteable that can be enabled or disabled. + When disabled, it will not write to its target. When enabled, it will. **/ -class VirtualTermWriter implements TermWriteable extends TermBuffer { - private static final defaultSize:Vec2 = {x: 50, y: 50}; - - private var target:TermWriteable; - private var enabled:Bool = false; - private var onResizeLink:CallbackLink; - - @:allow(kernel.ui) - private function new(?target:TermWriteable) { - setTarget(target); - - if (enabled) { - enable(); - } - - super(); - } - - public function enable() { - if (target != null) { - enabled = true; - super.copyBufferToTarget(target); - } - } - - public function disable() { - enabled = false; - } - - public inline function isEnabled():Bool { - return enabled; - } - - public function setTarget(newTarget:TermWriteable) { - if (newTarget != null) { - super.setSize(newTarget.getSize()); - - // Remove old target event listner - if (onResizeLink != null) { - onResizeLink.cancel(); - } - - // Add new target event listner - onResizeLink = newTarget.onResize.handle(newSize -> { - setSuperSize(newSize); - }); - - newTarget.reset(); - - target = newTarget; - } - } - - private function setSuperSize(size:Vec2) { - super.setSize(target.getSize()); - } - - // - // TermWriteable functions. - // - public override function write(text:String) { - if (isEnabled()) { - target.write(text); - } - - super.write(text); - } - - public override function scroll(y:Int) { - if (isEnabled()) { - target.scroll(y); - } - super.scroll(y); - } - - public override function getCursorPos():Pos { - if (isEnabled()) { - return target.getCursorPos(); - } else { - return super.getCursorPos(); - } - } - - public override function setCursorPos(x:Int, y:Int) { - if (isEnabled()) { - target.setCursorPos(x, y); - } - - super.setCursorPos(x, y); - } - - public override function getCursorBlink():Bool { - if (isEnabled()){ - return target.getCursorBlink(); - }else{ - return super.getCursorBlink(); - } - } - - public override function setCursorBlink(blink:Bool) { - if (isEnabled()){ - target.setCursorBlink(blink); - } - super.setCursorBlink(blink); - } - - public override function getSize():Vec2 { - // TODO: make sense ? - if (target != null) { - return target.getSize(); - } - - return defaultSize; - } - - public override function clear() { - if (isEnabled()) { - target.clear(); - } - - super.clear(); - } - - public override function clearLine() { - if (isEnabled()) { - target.clearLine(); - } - - super.clearLine(); - } - - public override function getTextColor():Color { - if (isEnabled()) { - return target.getTextColor(); - } - - return super.getTextColor(); - } - - public override function setTextColor(color:Color) { - if (isEnabled()) { - target.setTextColor(color); - } - - super.setTextColor(color); - } - - public override function getBackgroundColor():Color { - if (isEnabled()) { - return target.getBackgroundColor(); - } - - return super.getBackgroundColor(); - } - - public override function setBackgroundColor(color:Color) { - if (isEnabled()) { - target.setBackgroundColor(color); - } - - super.setBackgroundColor(color); - } - - public override function isColor():Bool { - throw new haxe.exceptions.NotImplementedException(); - } +interface VirtualTermWriter extends TermWriteable { + public function enable():Void; + public function disable():Void; + public function isEnabled():Bool; + public function setTarget(newTarget:TermWriteable):Void; } diff --git a/src/kernel/ui/WindowManager.hx b/src/kernel/ui/WindowManager.hx index 0db57f8..b1dabda 100644 --- a/src/kernel/ui/WindowManager.hx +++ b/src/kernel/ui/WindowManager.hx @@ -111,12 +111,26 @@ class WindowManager { }); } + /** + Same as createNewBufferedContext because it is the most simple context. + **/ public function createNewContext():WindowContext { - var newContext = new WindowContext(new VirtualTermWriter()); + return createNewBufferedContext(); + } + + public function createNewBufferedContext():WindowContext { + var newContext = new WindowContext(new BufferedVirtualTermWriter()); return newContext; } + public function createNewStatelessContext():{ctx:WindowContext, setRenderFunc:(() -> Void) -> Void, requestRender:Void->Void} { + var writer = new StatelessVirtualTermWriter(); + var newContext = new WindowContext(writer); + + return {ctx: newContext, setRenderFunc: writer.setRenderFunc, requestRender: writer.requestRender}; + } + public function getOutputs():ReadOnlyArray { var arr = Peripheral.instance.getScreens().map(screen -> return screen.getAddr()); arr.push("main"); diff --git a/src/lib/HomeContext.hx b/src/lib/HomeContext.hx index fcdcbc5..b3e8028 100644 --- a/src/lib/HomeContext.hx +++ b/src/lib/HomeContext.hx @@ -18,20 +18,23 @@ class HomeContext { private var ctx:WindowContext = null; private final workspaces:Map = []; private var currentWorkspace:Int = -1; - + private var requestRender: Void->Void; private var renderer:RootElement; public function new() {} public function run() { // Create main terminal context - ctx = WindowManager.instance.createNewContext(); + var stateless = WindowManager.instance.createNewStatelessContext(); + ctx = stateless.ctx; + requestRender = stateless.requestRender; WindowManager.instance.focusContextToOutput(ctx, "main"); renderer = new RootElement(); ctx.delegateEvents(renderer); - render(); + stateless.setRenderFunc(this.render); + requestRender(); // Register global key bindings to react to main terminal KernelEvents.instance.onKey.handle(e -> { @@ -71,7 +74,7 @@ class HomeContext { } private function focusMainTerm() { - render(); + requestRender(); WindowManager.instance.focusContextToOutput(ctx, "main"); currentWorkspace = -1; } @@ -80,7 +83,7 @@ class HomeContext { if (workspaces[contextId] != null) { focusContext(contextId); } else { - var newContext = WindowManager.instance.createNewContext(); + var newContext = WindowManager.instance.createNewBufferedContext(); // Create new terminal var term = new Terminal(); @@ -93,6 +96,7 @@ class HomeContext { private function render() { ctx.clear(); + ctx.setCursorBlink(false); var list = [ for (i in 0...MAX_CONTEXT) workspaces.exists(i) ? 'Switch to context ${i}' : 'Create new Terminal on context ${i}'