diff --git a/Nasal/canvas/gui.nas b/Nasal/canvas/gui.nas index 0313760fe..93d60d5e8 100644 --- a/Nasal/canvas/gui.nas +++ b/Nasal/canvas/gui.nas @@ -92,6 +92,7 @@ var Window = { # Destructor del: func { + me._title.del(); me.clearFocus(); if( me["_canvas"] != nil ) @@ -288,9 +289,9 @@ var Window = { var suffix = me._focused ? "" : "-unfocused"; me._title_bar_bg.set("fill", style.getColor("title" ~ suffix)); - me._frame.set("stroke", style.getColor("title" ~ suffix)); - me._title.set( "fill", style.getColor("title-text" ~ suffix)); - me._top_line.set( "stroke", style.getColor("title-highlight" ~ suffix)); + me._frame.setStroke(style.getColor("title" ~ suffix)); + me._title.setColor(style.getColor("title-text" ~ suffix)); + me._top_line.setStroke(style.getColor("title-highlight" ~ suffix)); me.getCanvasDecoration() .data("focused", me._focused) @@ -457,17 +458,15 @@ var Window = { button_close.listen("clicked", func me.del()); # title - me._title = title_bar.createChild("text", "title") - .set("alignment", "left-center") - .set("character-size", 14) - .set("font", "LiberationFonts/LiberationSans-Bold.ttf") - .setTranslation( int(x + 1.5 * w + 0.5), - int(y + 0.5 * h + 0.5) ); - var title = me.get("title", "Canvas Dialog"); + me._title = title_bar.createChild("text", "title") + .setText(title) + .setAlignment("left-center") + .set("character-size", 14) + .setFont("LiberationFonts/LiberationSans-Bold.ttf") + .setTranslation(int(x + 1.5 * w + 0.5), int(y + 0.5 * h + 0.5)); + me._node.getNode("title", 1).alias(me._title._node.getPath() ~ "/text"); - me.set("title", title); - title_bar.addEventListener("drag", func(e) me.move(e.deltaX, e.deltaY)); me._resizeDecoration(); @@ -495,7 +494,7 @@ var Window = { .moveTo(border_radius - 2, 2) .lineTo(me.get("size[0]") - border_radius + 2, 2); } -}; +}; #Window # Clear focus on click outside any window getDesktop().addEventListener("mousedown", func { diff --git a/Nasal/canvas/svg.nas b/Nasal/canvas/svg.nas index e1914bbdd..81e58ad36 100644 --- a/Nasal/canvas/svg.nas +++ b/Nasal/canvas/svg.nas @@ -725,7 +725,7 @@ var parsesvg = func(group, path, options = nil) id = tspan.attr['id']; pushElement('text', id); - stack[-1].set("text", tspan.text); + stack[-1].setText(tspan.text); if( x != 0 or y != 0 ) stack[-1].setTranslation(x, y); diff --git a/Nasal/debug.nas b/Nasal/debug.nas index 431d57afd..1fdca34ee 100644 --- a/Nasal/debug.nas +++ b/Nasal/debug.nas @@ -7,8 +7,12 @@ # debug.local([]) ... dump local variables of current # or given frame # -# debug.backtrace([]} ... writes backtrace with local variables -# (similar to gdb's "bt full) +# debug.backtrace([], [], []} +# ... writes backtrace (similar to gdb's "bt full) +# dump=0: only call stack +# dump=1 (default): with local variables +# skip_level: remove this many levels from +# call stack # # debug.proptrace([]]) ... trace property write/add/remove # events under the subtree for @@ -264,15 +268,16 @@ var local = func(frame = 0) { } -var backtrace = func(desc = nil) { - var d = desc == nil ? "" : " '" ~ desc ~ "'"; - print("\n" ~ _title("\n### backtrace" ~ d ~ " ###")); - for (var i = 1; 1; i += 1) { - if ((var v = caller(i)) == nil) - return; - print(_section(sprintf("#%-2d called from %s, line %s:", i - 1, v[2], v[3]))); - dump(v[0]); - } +var backtrace = func(desc = nil, dump_vars = 1, skip_level = 0) { + var d = (desc == nil) ? "" : " '" ~ desc ~ "'"; + print(""); + print(_title("### backtrace" ~ d ~ " ###")); + skip_level += 1; + for (var i = skip_level; 1; i += 1) { + if ((var v = caller(i)) == nil) return; + print(_section(sprintf("#%-2d called from %s, line %s:", i - skip_level, v[2], v[3]))); + if (dump_vars) dump(v[0]); + } } var bt = backtrace; @@ -433,6 +438,79 @@ var isnan = func { return !!size(err); } +# Breakpoint (BP) - do conditional backtrace (BT) controlled via property tree +# * count how often the BP was hit +# * do only a limited number of BT, avoid flooding the log / console +# +# Data can be viewed / modified in the prop tree /_debug/nas/bp-/* +# * tokens: number of backtraces to do; each hit will decrease this by 1 +# * hits: total number of hits +# +# == Example == +# var myBP = debug.Breakpoint.new("myLabel", 0); +# myBP.enable(4); # allow 4 hits, then be quiet +# +# #at the place of interest (e.g. in some loop or class method) insert: +# myBP.hit(); # do backtrace here if tokens > 0, reduce tokens by 1 +# +# print(myBP.getHits()); # print total number of hits +# +var Breakpoint = { + # label: Used in property path and as text for backtrace. + # dump_locals: bool passed to backtrace. Dump variables in BT. + new: func(label, dump_locals = 1) { + var obj = { + parents: [Breakpoint], + _tokensN: nil, + tokens: 0, + _hitsN: nil, + hits: 0, + label: "", + dump_locals: num(dump_locals), + }; + label = globals.string.replace(label, " ", "_"); + obj.label = globals.string.replace(label, "/", "_"); + var prop_path = "/_debug/nas/bp-"~obj.label~"/"; + obj._tokensN = props.globals.getNode(prop_path~"token", 1); + obj._hitsN = props.globals.getNode(prop_path~"hits", 1); + obj._hitsN.setIntValue(0); + obj.disable(); + return obj; + }, + + # enable BP and set hit limit; + # tokens: int > 0; default: 1 (single shot); 0 allowed (=disable); + enable: func(tokens = 1) { + if (num(tokens) == nil) tokens = 1; + if (tokens < 0) tokens = 0; + me.tokens = tokens; + me._tokensN.setIntValue(tokens); + return me; + }, + + # set tokens to zero, disables backtrace in hit() + disable: func { + me._tokensN.setIntValue(0); + return me; + }, + + # get total number of hits (not #backtraces done) + getHits: func { + return me.hits; + }, + + # hit the breakpoint, e.g. do backtrace if we have tokens available + hit: func() { + me.hits += 1; + me._hitsN.setIntValue(me.hits); + me.tokens = me._tokensN.getValue(); + if (me.tokens > 0) { + debug.backtrace(me.label, me.dump_locals, 1); + me._tokensN.setValue(me.tokens - 1); + } + return me; + }, +}; # --prop:debug=1 enables debug mode with additional warnings #