some ui stuff
This commit is contained in:
parent
4f881117cf
commit
c390519393
@ -40,6 +40,7 @@ class StatelessVirtualTermWriter implements VirtualTermWriter {
|
||||
}
|
||||
|
||||
if (enabled) {
|
||||
target.reset();
|
||||
renderFunc();
|
||||
renderRequested = false;
|
||||
}else{
|
||||
@ -84,6 +85,8 @@ class StatelessVirtualTermWriter implements VirtualTermWriter {
|
||||
});
|
||||
|
||||
target = newTarget;
|
||||
|
||||
target.reset();
|
||||
}
|
||||
|
||||
public function isEnabled():Bool {
|
||||
|
@ -46,6 +46,7 @@ class HomeContext {
|
||||
WindowManager.instance.focusContextToOutput(ctx, "main");
|
||||
|
||||
renderer = new RootElement();
|
||||
renderer.setTitle("Home");
|
||||
|
||||
ctx.delegateEvents(renderer);
|
||||
stateless.setRenderFunc(this.render);
|
||||
@ -146,7 +147,6 @@ class HomeContext {
|
||||
}
|
||||
|
||||
private function render() {
|
||||
ctx.clear();
|
||||
ctx.setCursorBlink(false);
|
||||
|
||||
var workspaceIDs:Array<Int> = [for (k=>v in workspaces) k];
|
||||
@ -163,11 +163,11 @@ class HomeContext {
|
||||
));
|
||||
}
|
||||
|
||||
children.push(new TextElement('Output :${selectedOutput}', {onClick: this.cycleOutput}));
|
||||
children.push(new TextElement('Output: ${selectedOutput}', {onClick: this.cycleOutput}));
|
||||
children.push(new TextElement('Exit', {onClick: KernelEvents.instance.shutdown}));
|
||||
|
||||
renderer.setChildren(children);
|
||||
|
||||
renderer.render().renderToContext(ctx);
|
||||
renderer.render(ctx.getSize()).renderToContext(ctx);
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package lib.ui.elements;
|
||||
class RootElement implements UIElement {
|
||||
private var children:Array<UIElement>;
|
||||
private final eventManager:UIEventManager = new UIEventManager();
|
||||
private var title:String = "";
|
||||
|
||||
public function new(?children:Array<UIElement>) {
|
||||
if (children == null) {
|
||||
@ -20,14 +21,21 @@ class RootElement implements UIElement {
|
||||
this.children = children;
|
||||
}
|
||||
|
||||
public function render():Canvas {
|
||||
public function render(bounds: Pos):Canvas {
|
||||
var canvas = new Canvas();
|
||||
var offset = new Pos({x: 0, y: 0});
|
||||
|
||||
if (hasTitle()) {
|
||||
var title = new TextElement(this.title);
|
||||
var halfWidth = Math.floor(bounds.x / 2) - Math.floor(this.title.length / 2);
|
||||
canvas.combine(title.render(bounds), {x: halfWidth, y: offset.y});
|
||||
offset = new Pos({x: 0, y: 1});
|
||||
}
|
||||
|
||||
this.eventManager.clearMap();
|
||||
|
||||
for (child in children) {
|
||||
var childCanvas = child.render();
|
||||
var childCanvas = child.render(bounds);
|
||||
var bounds = childCanvas.getBounds();
|
||||
bounds.offset(offset);
|
||||
|
||||
@ -39,4 +47,12 @@ class RootElement implements UIElement {
|
||||
|
||||
return canvas;
|
||||
}
|
||||
|
||||
public function setTitle(title: String) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
private inline function hasTitle(): Bool {
|
||||
return title != "";
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ class TextElement implements UIElement {
|
||||
return uiEvents;
|
||||
}
|
||||
|
||||
public function render():Canvas {
|
||||
public function render(bounds: Pos):Canvas {
|
||||
var canvas = new Canvas();
|
||||
for (i in 0...this.text.length) {
|
||||
var c = this.text.charAt(i);
|
||||
|
@ -3,5 +3,5 @@ package lib.ui.elements;
|
||||
import lib.ui.rendere.UIEventDelegate;
|
||||
|
||||
interface UIElement extends UIEventDelegate {
|
||||
public function render():Canvas;
|
||||
public function render(bounds: Pos):Canvas;
|
||||
}
|
||||
|
@ -1,74 +0,0 @@
|
||||
package lib.ui.reactive;
|
||||
|
||||
import lib.Pos;
|
||||
import util.Rect;
|
||||
import util.ObservableArray;
|
||||
import lib.Vec.Vec2;
|
||||
|
||||
class ListElement extends UIElement {
|
||||
|
||||
private final content:ObservableArray<UIElement>;
|
||||
private var elementMap: Map<Rect,UIElement> = new Map(); // Position in the map is relative.
|
||||
private var offset:Pos;
|
||||
|
||||
public function new(content: ObservableArray<UIElement>) {
|
||||
var events: UIEvents = {
|
||||
onClick: (p)->{
|
||||
var element = UIElement.getElementInMap((p.pos:Pos) - offset,elementMap);
|
||||
if (element != null)
|
||||
if (element.eventListner.onClick != null)
|
||||
element.eventListner.onClick.invoke(p);
|
||||
},
|
||||
onMouseUp: (p)->{
|
||||
var element = UIElement.getElementInMap((p.pos:Pos) - offset,elementMap);
|
||||
if (element != null)
|
||||
if (element.eventListner.onMouseUp != null)
|
||||
element.eventListner.onMouseUp.invoke(p);
|
||||
},
|
||||
onMouseScroll: (p)->{
|
||||
var element = UIElement.getElementInMap((p.pos:Pos) - offset,elementMap);
|
||||
if (element != null)
|
||||
if (element.eventListner.onMouseScroll != null)
|
||||
element.eventListner.onMouseScroll.invoke(p);
|
||||
},
|
||||
};
|
||||
|
||||
super(events);
|
||||
|
||||
this.content = content;
|
||||
this.content.subscribe(value -> {
|
||||
this.changedTrigger.trigger(null);
|
||||
// TODO: subscribe to elements and forward onChange event
|
||||
});
|
||||
}
|
||||
|
||||
public function render(bounds:Vec2<Int>,offset: Pos):Canvas {
|
||||
var canvas: Canvas = new Canvas();
|
||||
var writePoint:Pos = {x: 0, y: 0};
|
||||
this.offset = offset;
|
||||
|
||||
for(element in this.content.get()){
|
||||
if (bounds.y - writePoint.y <= 0) {
|
||||
// No more space to render children
|
||||
break;
|
||||
}
|
||||
|
||||
var childRender = element.render({
|
||||
x: bounds.x,
|
||||
y: bounds.y - writePoint.y
|
||||
}, offset + writePoint);
|
||||
|
||||
canvas.combine(childRender, writePoint);
|
||||
elementMap.set(new Rect(writePoint,
|
||||
{
|
||||
x: childRender.maxWidth() + writePoint.x,
|
||||
y: writePoint.y + (childRender.height() - 1),
|
||||
}
|
||||
),element);
|
||||
|
||||
writePoint = {x: 0, y: writePoint.y + childRender.height()};
|
||||
}
|
||||
|
||||
return canvas;
|
||||
}
|
||||
}
|
@ -1,170 +0,0 @@
|
||||
package lib.ui.reactive;
|
||||
|
||||
import util.Pos;
|
||||
import util.Rect;
|
||||
import util.Color;
|
||||
import util.Vec.Vec2;
|
||||
import kernel.ui.WindowContext;
|
||||
|
||||
using tink.CoreApi;
|
||||
|
||||
class ReactiveUI {
|
||||
private final context:WindowContext;
|
||||
private final children:Array<UIElement>;
|
||||
private var elementMap: Map<Rect,UIElement> = new Map();
|
||||
|
||||
public function new(context:WindowContext, children:Array<UIElement>) {
|
||||
this.context = context;
|
||||
this.children = children;
|
||||
|
||||
for (child in children) {
|
||||
child.changed.handle(_ -> {
|
||||
context.reset();
|
||||
render();
|
||||
});
|
||||
}
|
||||
|
||||
setupEvents();
|
||||
}
|
||||
|
||||
private function setupEvents() {
|
||||
context.onClick.handle(params ->{
|
||||
for (k => v in elementMap){
|
||||
if (k.isInside(params.pos)){
|
||||
if (v.eventListner.onClick != null){
|
||||
v.eventListner.onClick.invoke(params);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// context.onKey.handle(params ->{
|
||||
// for (k => v in elementMap){
|
||||
// if (k.isInside(params.pos)){
|
||||
// if (v.eventListner.onKey != null){
|
||||
// v.eventListner.onKey.invoke(params);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
|
||||
// context.onKeyUp.handle(params ->{
|
||||
// for (k => v in elementMap){
|
||||
// if (k.isInside(params.pos)){
|
||||
// if (v.eventListner.onKeyUp != null){
|
||||
// v.eventListner.onKeyUp.invoke(params);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
|
||||
// context.onMouseDrag.handle(params ->{
|
||||
// for (k => v in elementMap){
|
||||
// if (k.isInside(params.pos)){
|
||||
// if (v.eventListner.onMouseDrag != null){
|
||||
// v.eventListner.onMouseDrag.invoke(params);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
|
||||
context.onMouseScroll.handle(params ->{
|
||||
for (k => v in elementMap){
|
||||
if (k.isInside(params.pos)){
|
||||
if (v.eventListner.onMouseScroll != null){
|
||||
v.eventListner.onMouseScroll.invoke(params);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
context.onMouseUp.handle(params ->{
|
||||
for (k => v in elementMap){
|
||||
if (k.isInside(params.pos)){
|
||||
if (v.eventListner.onMouseUp != null){
|
||||
v.eventListner.onMouseUp.invoke(params);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// context.onPaste.handle(params ->{
|
||||
// for (k => v in elementMap){
|
||||
// if (k.isInside(params.pos)){
|
||||
// if (v.eventListner.onPaste != null){
|
||||
// v.eventListner.onPaste.invoke(params);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
|
||||
}
|
||||
|
||||
public function render() {
|
||||
var size = context.getSize();
|
||||
var result = renderChildren(children, size);
|
||||
this.elementMap = result.map;
|
||||
|
||||
writeToContext(result.canvas);
|
||||
}
|
||||
|
||||
private function writeToContext(screen:Canvas) {
|
||||
var currentBg:Color = Black;
|
||||
var currentFg:Color = White;
|
||||
|
||||
var currentLine = 0;
|
||||
|
||||
context.setBackgroundColor(currentBg);
|
||||
context.setTextColor(currentFg);
|
||||
context.setCursorPos(0, 0);
|
||||
|
||||
for (key => pixel in screen) {
|
||||
if (key.y != currentLine) {
|
||||
currentLine = key.y;
|
||||
context.setCursorPos(key.x, key.y);
|
||||
}
|
||||
|
||||
if (pixel == null) {
|
||||
context.write(' ');
|
||||
} else {
|
||||
if (pixel.bg != currentBg) {
|
||||
context.setBackgroundColor(pixel.bg);
|
||||
currentBg = pixel.bg;
|
||||
}
|
||||
|
||||
if (pixel.textColor != currentFg) {
|
||||
context.setTextColor(pixel.textColor);
|
||||
currentFg = pixel.textColor;
|
||||
}
|
||||
|
||||
context.write(pixel.char);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function renderChildren(children:Array<UIElement>, bounds:Vec2<Int>):{canvas: Canvas, map: Map<Rect,UIElement>} {
|
||||
var canvas:Canvas = new Canvas();
|
||||
var elementMap: Map<Rect,UIElement> = new Map();
|
||||
|
||||
var writePoint:Pos = {x: 0, y: 0};
|
||||
|
||||
for (child in children) {
|
||||
if (bounds.y - writePoint.y <= 0) {
|
||||
// No more space to render children
|
||||
break;
|
||||
}
|
||||
|
||||
var childRender = child.render({
|
||||
x: bounds.x,
|
||||
y: bounds.y - writePoint.y
|
||||
},writePoint);
|
||||
|
||||
canvas.combine(childRender, writePoint);
|
||||
elementMap.set(new Rect(writePoint,{x: childRender.maxWidth() + writePoint.x, y: writePoint.y + (childRender.height() - 1)}),child);
|
||||
|
||||
writePoint = {x: 0, y: writePoint.y + childRender.height()};
|
||||
}
|
||||
|
||||
return {canvas: canvas,map: elementMap};
|
||||
}
|
||||
}
|
@ -1,77 +0,0 @@
|
||||
package lib.ui.reactive;
|
||||
|
||||
import lib.ui.Dimensions;
|
||||
import util.ObjMerge;
|
||||
import lib.Pos;
|
||||
import util.Observable;
|
||||
import lib.Color;
|
||||
import lib.Vec.Vec2;
|
||||
|
||||
using tink.CoreApi;
|
||||
|
||||
typedef TextElementArgs = {
|
||||
public var ?text:Observable<String>;
|
||||
public var ?simpleText: String;
|
||||
public var ?bg:Color;
|
||||
public var ?fg:Color;
|
||||
public var ?padding: Dimensions;
|
||||
public var ?margin: Dimensions;
|
||||
}
|
||||
|
||||
class TextElement extends UIElement {
|
||||
private var args:TextElementArgs;
|
||||
|
||||
public function new(args: TextElementArgs,events: UIEvents = null) {
|
||||
super(events);
|
||||
this.args = args;
|
||||
|
||||
if (args.text != null) {
|
||||
args.text.subscribe(value -> {
|
||||
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 {
|
||||
var rtn = new Canvas();
|
||||
|
||||
var fullText = getText();
|
||||
|
||||
var writePoint: Pos = {x: 0,y: 0};
|
||||
|
||||
for (pos in 0...fullText.length) {
|
||||
var char = fullText.charAt(pos);
|
||||
|
||||
if (char == "\n"){
|
||||
writePoint = {x: 0, y: writePoint.y + 1};
|
||||
continue;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
@ -1,60 +0,0 @@
|
||||
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,47 +0,0 @@
|
||||
package lib.ui.reactive;
|
||||
|
||||
import util.ObjMerge;
|
||||
import lib.Pos;
|
||||
import lib.Rect;
|
||||
import lib.Vec.Vec2;
|
||||
|
||||
using tink.CoreApi;
|
||||
|
||||
abstract class UIElement {
|
||||
/**
|
||||
Render the element inside the bounds. `offset` is the offset to the parents position
|
||||
and can be used to calculate the absolute position of element.
|
||||
Just save `offset` and pass it to the children.
|
||||
**/
|
||||
abstract public function render(bounds:Vec2<Int>, offset: Pos):Canvas;
|
||||
public var changed(default, null):Signal<Noise>;
|
||||
private final changedTrigger:SignalTrigger<Noise> = Signal.trigger();
|
||||
public final eventListner:UIEvents = {};
|
||||
|
||||
public function new(events: UIEvents = null) {
|
||||
changed = changedTrigger.asSignal();
|
||||
if (events != null){
|
||||
this.eventListner = events;
|
||||
}
|
||||
}
|
||||
|
||||
public static function getElementInMap(pos: Pos,elementMap: Map<Rect,UIElement>):Null<UIElement>{
|
||||
for (k => v in elementMap){
|
||||
if (k.isInside(pos)){
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user