font caret underlining; repl line editing

This commit is contained in:
John Alanbrook 2023-10-05 13:02:12 +00:00
parent 5578b0f7e4
commit 8c69dfd71f
8 changed files with 128 additions and 65 deletions

View file

@ -17,7 +17,7 @@ Reflect = {};
Symbol = {};
URIError = {};
Object.complete_assign = function(target, source)
Object.mixin = function(target, source)
{
if (typeof source === 'undefined') return target;
@ -150,7 +150,7 @@ Object.copy = function(proto, ...objs)
{
var c = Object.create(proto);
for (var obj of objs)
Object.complete_assign(c, obj);
Object.mixin(c, obj);
return c;
}
@ -160,15 +160,17 @@ Object.defHidden = function(obj, prop)
Object.defineProperty(obj, prop, {enumerable:false, writable:true});
}
Object.hide = function(obj,prop)
Object.hide = function(obj,...props)
{
var p = Object.getOwnPropertyDescriptor(obj,prop);
if (!p) {
Log.warn(`No property of name ${prop}.`);
return;
for (var prop of props) {
var p = Object.getOwnPropertyDescriptor(obj,prop);
if (!p) {
Log.warn(`No property of name ${prop}.`);
return;
}
p.enumerable = false;
Object.defineProperty(obj, prop, p);
}
p.enumerable = false;
Object.defineProperty(obj, prop, p);
}
Object.defineProperty(Object.prototype, 'obscure', {

View file

@ -30,17 +30,17 @@ component.sprite = Object.copy(component, {
layer:0,
enabled:true,
path: "",
rect: {s0:0, s1: 1, t0: 0, t1: 1},
toString() { return "sprite"; },
make(go) {
var nsprite = Object.create(this);
nsprite.gameobject = go;
Object.assign(nsprite, make_sprite(go.body));
Object.complete_assign(nsprite, component.sprite.impl);
Object.mixin(nsprite, component.sprite.impl);
Object.hide(nsprite, 'gameobject', 'id');
for (var p in component.sprite.impl)
if (typeof this[p] !== 'undefined') {
Log.warn(`setting ${p}`);
if (typeof this[p] !== 'undefined')
nsprite[p] = this[p];
}
return nsprite;
},
});
@ -55,7 +55,6 @@ component.sprite.impl = {
hide() { this.enabled = false; },
show() { this.enabled = true; },
asset(str) { this.path = str; },
rect: {s0:0, s1: 1, t0: 0, t1: 1},
get enabled() { return cmd(114,this.id); },
set enabled(x) { cmd(20,this.id,x); },
set color(x) { cmd(96,this.id,x); },
@ -705,8 +704,8 @@ component.circle2d = Object.copy(collider2d, {
var circle = Object.create(this);
circle.gameobject = go;
Object.assign(circle, make_circle2d(go.body));
Object.complete_assign(circle,this.impl);
Object.mixin(circle,this.impl);
Object.hide(circle, 'gameobject', 'id', 'shape', 'scale');
return circle;
},
});

View file

@ -88,6 +88,45 @@ function positive_diff(from, to)
var diff = {};
}
function vdiff(from,to)
{
if (typeof from === 'number') {
var a = Number.prec(from);
return a === to ? undefined : a;
}
if (typeof from === 'object') {
var ret = {};
Object.keys(from).forEach(function(k) {
var diff = vdiff(from[k], to[k]);
if (diff) ret[k] = diff;
});
return ret.empty ? undefined : ret;
}
}
function gdiff(from, to) {
var obj = {};
Object.entries(from).forEach(function([k,v]) {
if (typeof v === 'function') return;
if (!Object.isAccessor(from, k)) {
obj[k] = v;
return;
}
var diff = vdiff(v, to[k]);
if (diff) {
if (Array.isArray(v))
obj[k] = Object.values(diff);
else
obj[k] = diff;
}
});
return obj;
};
function diff(from, to) {
var obj = {};
@ -102,10 +141,5 @@ function diff(from, to) {
}
}
for (var e in from) {
if (!to.hasOwnProperty(e))
obj[e] = "DELETE";
}
return obj;
};

View file

@ -402,7 +402,7 @@ var editor = {
lvlchain.forEach(function(x,i) {
lvlcolor = colormap.sample(lvlcolorsample);
var lvlstr = x.toString();
if (x.dirty)
if (x._ed.dirty)
lvlstr += "*";
if (i === lvlchain.length-1) lvlstr += "[this]";
GUI.text(lvlstr, [0, ypos], 1, lvlcolor);
@ -421,12 +421,12 @@ var editor = {
this.selectlist.forEach(function(x) {
var sname = x.__proto__.toString();
if (!x.level_obj().empty)
x.dirty = true;
if (!x.json_obj().empty)
x._ed.dirty = true;
else
x.dirty = false;
x._ed.dirty = false;
if (x.dirty) sname += "*";
if (x._ed.dirty) sname += "*";
GUI.text(sname, world2screen(x.worldpos()).add([0, 16]), 1, lvlcolor);
GUI.text(x.worldpos().map(function(x) { return Math.round(x); }), world2screen(x.worldpos()), 1, Color.white);
@ -788,7 +788,7 @@ editor.inputs['C-s'] = function() {
return;
};
if (editor.selectlist.length !== 1 || !editor.selectlist[0].dirty) return;
if (editor.selectlist.length !== 1 || !editor.selectlist[0]._ed.dirty) return;
Object.merge(editor.selectlist[0].ur, editor.selectlist[0].level_obj());
var path = editor.selectlist[0].ur.toString();
path = path.replaceAll('.','/');
@ -818,7 +818,7 @@ editor.inputs['M-t'] = function() { editor.edit_level.objects.forEach(function(x
editor.inputs['M-t'].doc = "Unlock all objects in current level.";
editor.inputs['C-n'] = function() {
if (editor.edit_level.dirty) {
if (editor.edit_level._ed.dirty) {
Log.info("Level has changed; save before starting a new one.");
editor.openpanel(gen_notify("Level is changed. Are you sure you want to close it?", _ => editor.clear_level()));
return;
@ -835,14 +835,14 @@ editor.inputs['C-o'].doc = "Open a level.";
editor.inputs['C-M-o'] = function() {
if (editor.selectlist.length === 1 && editor.selectlist[0].file) {
if (editor.edit_level.dirty) return;
if (editor.edit_level._ed.dirty) return;
editor.load(editor.selectlist[0].file);
}
};
editor.inputs['C-M-o'].doc = "Revert opened level back to its disk form.";
editor.inputs['C-S-o'] = function() {
if (!editor.edit_level.dirty)
if (!editor.edit_level._ed.dirty)
editor.load_prev();
};
editor.inputs['C-S-o'].doc = "Open previous level.";
@ -1331,13 +1331,19 @@ inputpanel.inputs = {};
inputpanel.inputs.char = function(c) {
this.value = this.value.slice(0,this.caret) + c + this.value.slice(this.caret);
this.caret++;
Log.warn(this.caret);
this.keycb();
}
inputpanel.inputs['C-d'] = function() { this.value = this.value.slice(0,this.caret) + this.value.slice(this.caret+1); };
inputpanel.inputs.tab = function() { this.value = tab_complete(this.value, this.assets); }
inputpanel.inputs.escape = function() { this.close(); }
inputpanel.inputs['C-b'] = function() {
if (this.caret === 0) return;
this.caret--;
};
inputpanel.inputs['C-a'] = function() { this.caret = 0; };
inputpanel.inputs['C-f'] = function() { this.caret = this.value.length; };
inputpanel.inputs.backspace = function() {
if (this.caret === 0) return;
this.value = this.value.slice(0,this.caret-1) + this.value.slice(this.caret);
this.caret--;
this.keycb();
@ -1413,6 +1419,7 @@ var replpanel = Object.copy(inputpanel, {
action() {
if (!this.value) return;
this.prevthis.unshift(this.value);
this.prevmark = -1;
var ecode = "";
var repl_obj = (editor.selectlist.length === 1) ? editor.selectlist[0] : editor.edit_level;
ecode += `var $ = repl_obj.objects;`;
@ -1435,6 +1442,7 @@ replpanel.inputs['C-p'] = function()
if (this.prevmark >= this.prevthis.length) return;
this.prevmark++;
this.value = this.prevthis[this.prevmark];
this.inputs['C-f'].call(this);
}
replpanel.inputs['C-n'] = function()
@ -1445,6 +1453,8 @@ replpanel.inputs['C-n'] = function()
this.value = "";
} else
this.value = this.prevthis[this.prevmark];
this.inputs['C-f'].call(this);
}
var objectexplorer = Object.copy(inputpanel, {
@ -1670,10 +1680,7 @@ var quitpanel = Object.copy(inputpanel, {
},
guibody () {
Nuke.label("Really quit?");
Nuke.newline(2);
if (Nuke.button("yes"))
this.submit();
return Mum.text({str: "Really quit?"});
},
});
@ -1848,6 +1855,3 @@ Game.stop();
Game.editor_mode(true);
load("editorconfig.js");
Log.warn(ur.ball);
Log.warn(ur['ball.big']);

View file

@ -204,6 +204,7 @@ var gameobject = {
velocity:[0,0],
angularvelocity:0,
layer:0,
save:true,
selectable:true,
ed_locked:false,
@ -290,11 +291,19 @@ var gameobject = {
if (ret.empty) return undefined;
return ret;
},
json_obj() {
var ur = gameobject.diff(this,this.__proto__);
return ur ? ur : {};
var d = gdiff(this,this.__proto__);
delete d.pos;
delete d.angle;
delete d.velocity;
delete d.angularvelocity;
d.components = [];
this.components.forEach(function(x) {
var c = gdiff(x, x.__proto__);
if (c) d.components.push(c);
});
return d;
},
transform_obj() {
@ -398,17 +407,20 @@ var gameobject = {
right() { return [1,0].rotate(Math.deg2rad(this.angle));},
left() { return [-1,0].rotate(Math.deg2rad(this.angle));},
instances: [],
make(level) {
level ??= Primum;
var obj = Object.create(this);
this.instances.push(obj);
obj.body = make_gameobject();
Object.hide(obj, 'body');
obj.components = {};
obj.objects = {};
Object.complete_assign(obj, gameobject.impl);
Object.mixin(obj, gameobject.impl);
Object.hide(obj, 'components');
Object.hide(obj, 'objects');
obj.toJSON = gameobject.level_json;
obj._ed = {};
Object.hide(obj, '_ed');
Game.register_obj(obj);
@ -416,6 +428,8 @@ var gameobject = {
obj.reparent(level);
Object.hide(obj, 'level')
for (var prop in this) {
var p = this[prop];
if (typeof p !== 'object') continue;
@ -423,10 +437,12 @@ var gameobject = {
if ('ur' in p) {
obj[prop] = obj.spawn(prototypes.get_ur(p.ur));
obj.rename_obj(obj[prop].toString(), prop);
Object.hide(obj, prop);
} else if ('comp' in p) {
Log.warn(p);
obj[prop] = Object.assign(component[p.comp].make(obj), p);
obj.components[prop] = obj[prop];
Object.hide(obj,prop);
}
};

View file

@ -117,8 +117,7 @@ Mum.text = Mum.extend({
this.calc_bb(cursor);
var aa = [0,1].sub(this.anchor);
var pos = cursor.add(this.wh.scale(aa)).add(this.offset);
if (this.caret > -1) Log.warn(`Drawing box at pos ${this.caret} over letter ${this.str[this.caret-1]}`);
ui_text(this.str, pos, this.font_size, this.color, -1, this.width, this.caret);
ui_text(this.str, pos, this.font_size, this.color, this.width, this.caret);
},
update_bb(cursor) {

View file

@ -1622,7 +1622,7 @@ void ffi_load() {
DUK_FUNC(register, 3)
DUK_FUNC(register_collide, 6)
DUK_FUNC(ui_text, 6)
DUK_FUNC(ui_text, 8)
DUK_FUNC(gui_img, 10)
DUK_FUNC(inflate_cpv, 3)

View file

@ -181,6 +181,9 @@ static int curchar = 0;
void draw_char_box(struct Character c, HMM_Vec2 cursor, float scale, struct rgba color)
{
cursor.Y -= 2;
sdrawCharacter(font->Characters['_'], cursor, scale, color);
return;
cpVect wh;
wh.x = 8 * scale;
@ -281,37 +284,44 @@ struct boundingbox text_bb(const char *text, float scale, float lw, float tracki
return cwh2bb((HMM_Vec2){0,0}, (HMM_Vec2){cursor.X,font->linegap-cursor.Y});
}
void check_caret(int caret, int l, HMM_Vec2 pos, float scale, struct rgba color)
{
if (caret == l)
draw_char_box(font->Characters[0], pos, scale, color);
}
/* pos given in screen coordinates */
int renderText(const char *text, HMM_Vec2 pos, float scale, struct rgba color, float lw, int caret, float tracking) {
int len = strlen(text);
HMM_Vec2 cursor = pos;
int l = 0;
const unsigned char *line, *wordstart, *drawstart;
line = drawstart = (unsigned char *)text;
struct rgba usecolor = color;
check_caret(caret, l, cursor, scale, usecolor);
while (*line != '\0') {
if (caret >= 0 && caret == curchar)
draw_char_box(font->Characters[69], cursor, scale, color);
if (isblank(*line)) {
sdrawCharacter(font->Characters[*line], cursor, scale, usecolor);
cursor.X += font->Characters[*line].Advance * tracking * scale;
line++;
} else if (isspace(*line)) {
sdrawCharacter(font->Characters[*line], cursor, scale, usecolor);
while (line[l] != '\0') {
if (isblank(line[l])) {
sdrawCharacter(font->Characters[line[l]], cursor, scale, usecolor);
cursor.X += font->Characters[line[l]].Advance * tracking * scale;
l++;
check_caret(caret, l, cursor, scale, usecolor);
} else if (isspace(line[l])) {
sdrawCharacter(font->Characters[line[l]], cursor, scale, usecolor);
cursor.Y -= scale * font->linegap;
cursor.X = pos.X;
line++;
l++;
check_caret(caret, l, cursor, scale, usecolor);
} else {
wordstart = line;
wordstart = &line[l];
int wordWidth = 0;
while (!isspace(*line) && *line != '\0') {
wordWidth += font->Characters[*line].Advance * tracking * scale;
line++;
while (!isspace(line[l]) && line[l] != '\0') {
wordWidth += font->Characters[line[l]].Advance * tracking * scale;
l++;
}
if (lw > 0 && (cursor.X + wordWidth - pos.X) >= lw) {
@ -319,15 +329,14 @@ int renderText(const char *text, HMM_Vec2 pos, float scale, struct rgba color, f
cursor.Y -= scale * font->linegap;
}
while (wordstart < line) {
while (wordstart < &line[l]) {
sdrawCharacter(font->Characters[*wordstart], cursor, scale, usecolor);
cursor.X += font->Characters[*wordstart].Advance * tracking * scale;
wordstart++;
check_caret(caret, wordstart-line, cursor, scale, usecolor);
}
}
}
// if (caret > curchar)
// draw_char_box(font->Characters[69], cursor, scale, color);
return cursor.Y - pos.Y;
}