Revision: 4304 http://vexi.svn.sourceforge.net/vexi/?rev=4304&view=rev Author: mkpg2 Date: 2011-12-12 01:05:02 +0000 (Mon, 12 Dec 2011) Log Message: ----------- New widget. Duration field.
Added Paths: ----------- trunk/org.vexi-vexi.widgets/src_main/org/vexi/lib/util/abstractlexer.t trunk/org.vexi-vexi.widgets/src_main/org/vexi/lib/widget/durationfield.t trunk/org.vexi-vexi.widgets/src_main/org/vexi/theme/classic/durationfield.t trunk/org.vexi-vexi.widgets/src_main/vexi/widget/durationfield.t trunk/org.vexi-vexi.widgets/src_poke/poke/widgets/durationfield.t trunk/org.vexi-vexi.widgets/src_test/test/widget/durationfield.t Added: trunk/org.vexi-vexi.widgets/src_main/org/vexi/lib/util/abstractlexer.t =================================================================== --- trunk/org.vexi-vexi.widgets/src_main/org/vexi/lib/util/abstractlexer.t (rev 0) +++ trunk/org.vexi-vexi.widgets/src_main/org/vexi/lib/util/abstractlexer.t 2011-12-12 01:05:02 UTC (rev 4304) @@ -0,0 +1,162 @@ +<!-- Copyright 2011 Web Enable IT -- all rights reserved --> + +<vexi xmlns:ui="vexi://js" xmlns:meta="vexi://meta"> + <meta:doc> + <author>Mike Goodwin</author> + <name></name> + <desc></desc> + <usage></usage> + </meta:doc> + + // constants + const C0 = "0".charCodeAt(0); + const C9 = "9".charCodeAt(0); + const CA = "A".charCodeAt(0); + const CZ = "Z".charCodeAt(0); + const Ca = "a".charCodeAt(0); + const Cz = "z".charCodeAt(0); + const C_ = "_".charCodeAt(0); + + // || c == '\u0020' || c == '\u0009' || c == '\u000C' || c == '\u000B' + static.isWhitespace = function(c) { return c ==" " || c == "\n" || c == "\r" || c=="\t"; }; + static.isDigit = function(code) { return code >= C0 and C9 >= code; } + static.isDigitOrDot = function(c) { + return c!=null and (isDigit(c.charCodeAt(0)) || c=="."); + } + static.isIdStart = function(code) { + return (code >= CA and CZ >= code) || (code >= Ca and Cz >= code) || code==C_; + }; + static.isId = function(c) { + if(c == null) return null; + const code = c.charCodeAt(0); + + //vexi.log.info(c + " " +code + " " + isIdStart(code) + " " + isDigit(code)); + return isIdStart(code) || isDigit(code); + }; + + + <ui:Object> + + thisobj.lexToken; // implement ... + + const cstack = []; // reader char stack + const tstack = []; // token stack + const ostack = []; // object stack (strings/numbers) + + var utf8reader; + thisobj.reader = {}; // Pushback Reader + + reader.c ++= function() { + if (cstack.length==0) return utf8reader["char"]; + else return cstack.pop(); + }; + + reader.c ++= function(v) { + cstack.push(v); + return; + }; + + reader.peek ++= function(v) { + var ret = reader.c; + reader.c = ret; + return ret; + }; + + + var tok; // last token parsed + thisobj.obj; // obj (string/number) + + thisobj.init = function(s) { + cstack.clear(); + tstack.clear(); + ostack.clear(); + + tok = null; + obj = null; + + // INIT + if (typeof(s)=="string") { + //vexi.log.info("Lexing: " + arguments[0]); + utf8reader = vexi.stream.utf8reader(vexi.stream.fromString(s)); + } else { + throw "expected expression string as argument, got " + typeof(s); + } + } + + + const isId = static.isId; + const isDigitOrDot = static.isDigitOrDot; + + thisobj.lexIdentifier = function(c) { + var arr = [c]; + while (isId(c = reader.c)) arr.push(c); + reader.c = c; //pushback + obj = arr.join(""); + var kw = keywords?[obj]; + if (kw) return kw; + tok = "NAME"; + return tok; + } + + thisobj.lexNumber = function(c) { + var arr = [c]; + while (isDigitOrDot(c = reader.c)) arr.push(c); + reader.c = c; //pushback + var str = arr.join(""); + if (str.indexOf(".") == -1) + obj = vexi.string.parseInt(str); + else obj = vexi.string.parseFloat(str); + tok = "NUMBER"; + return tok; + }; + + thisobj.lexString = function(c) { + var arr = []; + var quote = c; + + while ((c = reader.c) != quote) { + if (c == "\n" || c == null) throw "unterminated string literal"; + arr.push(c); + } + obj = arr.join(""); + tok = "STRING"; + return tok; + } + + thisobj.match = function(exp) { + var c = reader.c; + if (c == exp) return true; + reader.c = c; + }; + + thisobj.le = function(msg) { + throw msg; + }; + + thisobj.pushBack = function() { + ostack.push(obj); + tstack.push(tok); + }; + + thisobj.peekToken = function() { + var r = nextToken(); + ostack.push(obj); + tstack.push(r); + return r; + }; + + thisobj.nextToken = function() { + obj = null; + if (tstack.length>0) { + tok = tstack.pop(); + obj = ostack.pop(); + } else { + tok = lexToken(); + } + return tok; + }; + + + + </ui:Object> +</vexi> Added: trunk/org.vexi-vexi.widgets/src_main/org/vexi/lib/widget/durationfield.t =================================================================== --- trunk/org.vexi-vexi.widgets/src_main/org/vexi/lib/widget/durationfield.t (rev 0) +++ trunk/org.vexi-vexi.widgets/src_main/org/vexi/lib/widget/durationfield.t 2011-12-12 01:05:02 UTC (rev 4304) @@ -0,0 +1,197 @@ +<!-- Copyright 2011 - see COPYING for details [LGPL] --> + +<vexi xmlns:ui="vexi://ui" xmlns:meta="vexi://meta" xmlns="org.vexi.lib" xmlns:sync="vexi.util.sync"> + <meta:doc> + <author>Mike Goodwin</author> + </meta:doc> + + <role.focusable /> + <role.tooltipable /> + <text.field /> + <ui:box> + + thisbox.v_edit; // abstract + thisbox.enabled = true; + + thisbox.value; // value and the v_edit.text are synchronized + thisbox.defaultUnit = "h"; + thisbox.largestUnit = "h"; + + thisbox.duration2text = function(d){ + return static.formatDuration(d, largestUnit); + }; + + thisbox.text2duration = function(s) { + return static.parseDuration(s, defaultUnit); + } + + KeyPressed ++= static.keypressEvent; + focused ++= static.focusWrite; + v_edit ++= static.editWrite; + + + </ui:box> + + const units = { + list: [ + {"name": "y", "mult": 3600*24*365}, + {"name": "d", "mult": 3600*24}, + {"name": "h", "mult": 3600}, + {"name": "m", "mult": 60 }, + {"name": "s", "mult": 1} + ], + get: function(unit){ + var i = this.getIndex(unit); + return this.list[i]; + }, + getIndex: function(unit){ + for(var i,u in this.list){ + if(u==unit || u.name==unit) return i; + } + return null; + }, + getNextSmallest: function(unit){ + var i = this.getIndex(unit); + if(i!=null and this.list.length>i+1) return this.list[i+1]; + return null; + }, + isLarger: function(uA, uB){ + var iA = this.getIndex(uA); + var iB = this.getIndex(uB); + if(iA!=null and iB!=null){ + return iA>iB; + } + return false; + } + }; + + + static.formatDuration= function(d, largestUnit) { + if(d==null) return null; + var r = ""; + var i = units.getIndex(largestUnit); + for(; units.list.length>i; i++){ + var u = units.list[i]; + var remainder = d%u.mult; + var qty = (d-remainder)/u.mult; + if(qty>0) + r += ""+qty+u.name; + d = remainder; + } + return r; + } + + const isWhitespace = .util.abstractlexer..isWhitespace; + const isAlpha = .util.abstractlexer..isIdStart; + const isDigit = .util.abstractlexer..isDigit; + const lexer = new .util.abstractlexer(); + const lexUnit = function(c){ + var arr = [c]; + while (isAlpha(c = lexer.reader.c)) arr.push(c); + lexer.reader.c = c; //pushback + lexer.obj = arr.join(""); + return "UNIT"; + }; + + lexer.lexToken = function(){ + var c; + do{ + c = lexer.reader.c; + if (c==null) return null; + } while( isWhitespace(c) ); // || c == '\u0020' || c == '\u0009' || c == '\u000C' || c == '\u000B' + var code = c.charCodeAt(0); + if (isDigit(code)) return lexer.lexNumber(c); + if (isAlpha(code)) return lexUnit(c); + return null; + }; + + + + const isInt = function(n){ return n==vexi.math.round(n); } + const parseSegment = function(top, defaultUnit){ + defaultUnit = units.get(defaultUnit); + var tok = lexer.nextToken(); + if(tok==null) return 0; + if(tok!="NUMBER") return null; + var qty = lexer.obj; + + var tok2 = lexer.nextToken(); + if(tok2=="UNIT"){ + var unitSpecified = units.get(lexer.obj); + if(unitSpecified==null){ + return null; + }else{ + // cannot have larger units after smaller ones + if(!top and units.isLarger(unitSpecified,defaultUnit)){ + return null; + } + var r = unitSpecified.mult*qty; + if(isInt(qty)){ + var nextU = units.getNextSmallest(unitSpecified); + if(nextU!=null){ + var nextR = callee(false, nextU); + if(nextR==null) return null; + return r + nextR; + } + } + var tok3 = lexer.nextToken(); + if(tok3!=null) return null; + return r; + } + + }else if(tok2==null){ + return defaultUnit.mult*qty;; + } + } + + static.parseDuration = function(s, defaultUnit){ + lexer.init(s); + return parseSegment(true, defaultUnit); + } + + /** set up sync between edit.text and numfield.value */ + static.editWrite = function(v) { + cascade = v; + sync..syncTransform(trapee, v, "value", "text", trapee.duration2text, trapee.text2duration); + trapee[trapname] --= callee; + } + + /** when focus changes, self put text in case text content is updated */ + static.focusWrite = function(v) { + if (!v) { + // trigger value setting + trapee.value = trapee.text2duration(trapee.v_edit.text); + } + cascade = v; + } + + /** fire action or filter key in case of password fields */ + static.keypressEvent = function(v) { + var t = trapee; + var e = t.v_edit; + var txt0 = e.text; + + switch (v) { + case "ENTER": + case "enter": { + var cpos = e.getCursorCharIndex(); + t.value = t.text2duration(e.text); + cpos = vexi.math.max(e.text.length, cpos); + e.setCursorCharIndex(cpos); + return; + } + default: { + var cpos = e.getCursorCharIndex(); + cascade = v; + // If invalid revert ... + var value2 = t.text2duration(e.text); + if(value2==null){ + e.text = txt0; + e.setCursorCharIndex(cpos); + } + return; + } + } + } + +</vexi> Added: trunk/org.vexi-vexi.widgets/src_main/org/vexi/theme/classic/durationfield.t =================================================================== --- trunk/org.vexi-vexi.widgets/src_main/org/vexi/theme/classic/durationfield.t (rev 0) +++ trunk/org.vexi-vexi.widgets/src_main/org/vexi/theme/classic/durationfield.t 2011-12-12 01:05:02 UTC (rev 4304) @@ -0,0 +1,33 @@ +<!-- Copyright 2011 - see COPYING for details [LGPL] --> + +<vexi xmlns:ui="vexi://ui" xmlns:meta="vexi://meta" xmlns="vexi.theme" + xmlns:lib="org.vexi.lib"> + <meta:doc> + <author>Charles Goodwin</author> + </meta:doc> + + <lib:widget.durationfield /> + <lib:widget.shadowtext /> + <bevel redirect=":$content" form="down" margin="3" padding="3" vshrink="true"> + <ui:box id="inset" layout="place" minheight="19"> + <ui:box id="content" /> + <lib:layout.pad id="shadowpad" padding="3" vshrink="true"> + <ui:box id="shadow" align="right" textcolor="#999999" /> + </lib:layout.pad> + </ui:box> + + thisbox.th_bevel = thisbox; + thisbox.th_shadowtext = $shadow; + thisbox.th_shadowwrap = $shadowpad; + thisbox.th_minus = $minus; + thisbox.th_view = $inset; + thisbox.th_viewport = $content; + thisbox.v_init = static.init; + thisbox.v_prevfill = .settings..fill; + + </bevel> + <lib.finalize /> + + static.init = { textalign:"right" } + +</vexi> Added: trunk/org.vexi-vexi.widgets/src_main/vexi/widget/durationfield.t =================================================================== --- trunk/org.vexi-vexi.widgets/src_main/vexi/widget/durationfield.t (rev 0) +++ trunk/org.vexi-vexi.widgets/src_main/vexi/widget/durationfield.t 2011-12-12 01:05:02 UTC (rev 4304) @@ -0,0 +1,27 @@ +<!-- Copyright 2009 - see COPYING for details [LGPL] --> + +<vexi xmlns:ui="vexi://ui" xmlns:meta="vexi://meta" xmlns:theme="vexi.theme" + xmlns="org.vexi.lib.layout"> + <meta:doc> + <author>Charles Goodwin</author> + <name>Textfield</name> + <desc>An editable single-line textfield</desc> + <usage> + Has the following specific properties: + - enabled (boolean) + If set to false, users can not edit the content + - maxlength (int) + Prevents the textfield content growing longer than + maxlength + - password (boolean) + A passworded textfield obscures the input text as *s + Read 'value' to get the password from the textfield + </usage> + </meta:doc> + + <margin /> + <theme:durationfield /> + <pad /> + <editbox /> + <container /> +</vexi> Added: trunk/org.vexi-vexi.widgets/src_poke/poke/widgets/durationfield.t =================================================================== --- trunk/org.vexi-vexi.widgets/src_poke/poke/widgets/durationfield.t (rev 0) +++ trunk/org.vexi-vexi.widgets/src_poke/poke/widgets/durationfield.t 2011-12-12 01:05:02 UTC (rev 4304) @@ -0,0 +1,15 @@ + +<vexi xmlns:ui="vexi://ui" + xmlns:w="vexi.widget" + xmlns:sync="vexi.util.sync"> + <w:surface /> + <ui:box orient="vertical"> + + <w:numfield id="number" /> + <w:durationfield id="duration" /> + sync..sync($number,$duration,"value","value"); + + vexi.ui.frame = thisbox; + + </ui:box> +</vexi> Added: trunk/org.vexi-vexi.widgets/src_test/test/widget/durationfield.t =================================================================== --- trunk/org.vexi-vexi.widgets/src_test/test/widget/durationfield.t (rev 0) +++ trunk/org.vexi-vexi.widgets/src_test/test/widget/durationfield.t 2011-12-12 01:05:02 UTC (rev 4304) @@ -0,0 +1,38 @@ +<vexi xmlns:meta="vexi://meta" xmlns:ui="vexi://ui" xmlns="vexi.widget"> + <meta:doc> + <author>Charles Goodwin</author> + </meta:doc> + + /// Quick Suite + var suite = {}; + + var vunit = vexi..vexi.test.vunit; + var assertEq = vunit..assertEq; + var assertExceptionThrown = vunit..assertExceptionThrown; + + suite.testBasic = function() { + /** + * basic numfield value write tests + */ + var b = new .durationfield(); + b.value = 123; + assertEq("2m3s",b.text); + b.text = "2m4s"; + assertEq(124,b.value); + b.text = "2m5"; + assertEq(125,b.value); + b.text = "1"; + assertEq(3600,b.value); + }; + + suite.testEmpty = function() { + var b = new .durationfield(); + b.focused = true; + b.KeyPressed = "enter"; + }; + + static.test = suite; + + <surface/> + +</vexi> \ No newline at end of file This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. ------------------------------------------------------------------------------ Learn Windows Azure Live! Tuesday, Dec 13, 2011 Microsoft is holding a special Learn Windows Azure training event for developers. It will provide a great way to learn Windows Azure and what it provides. You can attend the event by watching it streamed LIVE online. Learn more at http://p.sf.net/sfu/ms-windowsazure _______________________________________________ Vexi-svn mailing list Vexi-svn@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/vexi-svn