diff --git a/controllers/controller.boolean.js b/controllers/controller.boolean.js index 8c52605..66808ca 100644 --- a/controllers/controller.boolean.js +++ b/controllers/controller.boolean.js @@ -1,7 +1,7 @@ -var BooleanController = function() { +GUI.BooleanController = function() { this.type = "boolean"; - Controller.apply(this, arguments); + GUI.Controller.apply(this, arguments); var _this = this; var input = document.createElement('input'); @@ -32,9 +32,8 @@ var BooleanController = function() { val = eval(val); } catch (e) {} } - return Controller.prototype.setValue.call(this, val); + return GUI.Controller.prototype.setValue.call(this, val); } }; -BooleanController.prototype = new Controller(); -BooleanController.prototype.constructor = BooleanController; +GUI.extendController(GUI.BooleanController); \ No newline at end of file diff --git a/controllers/controller.function.js b/controllers/controller.function.js index c95c5ec..bf17507 100644 --- a/controllers/controller.function.js +++ b/controllers/controller.function.js @@ -1,12 +1,11 @@ -var FunctionController = function() { +GUI.FunctionController = function() { this.type = "function"; var that = this; - Controller.apply(this, arguments); + GUI.Controller.apply(this, arguments); this.domElement.addEventListener('click', function() { that.object[that.propertyName].call(that.object); }, false); this.domElement.style.cursor = "pointer"; this.propertyNameElement.style.cursor = "pointer"; }; -FunctionController.prototype = new Controller(); -FunctionController.prototype.constructor = FunctionController; +GUI.extendController(GUI.FunctionController); diff --git a/controllers/controller.js b/controllers/controller.js index be4510c..d5b4047 100644 --- a/controllers/controller.js +++ b/controllers/controller.js @@ -1,4 +1,4 @@ -var Controller = function() { +GUI.Controller = function() { this.parent = arguments[0]; this.object = arguments[1]; @@ -14,38 +14,35 @@ var Controller = function() { this.name(this.propertyName); this.domElement.appendChild(this.propertyNameElement); - this.timeElement = document.createElement('div'); - this.timeElement.setAttribute('class', 'guidat-time'); - this.domElement.appendChild(this.timeElement); GUI.makeUnselectable(this.domElement); }; -Controller.prototype.changeFunction = null; +GUI.Controller.prototype.changeFunction = null; -Controller.prototype.name = function(n) { +GUI.Controller.prototype.name = function(n) { this.propertyNameElement.innerHTML = n; return this; }; -Controller.prototype.reset = function() { +GUI.Controller.prototype.reset = function() { this.setValue(this.initialValue); return this; }; -Controller.prototype.listen = function() { +GUI.Controller.prototype.listen = function() { this.parent.listenTo(this); return this; } -Controller.prototype.unlisten = function() { +GUI.Controller.prototype.unlisten = function() { this.parent.unlistenTo(this); // <--- hasn't been tested yet return this; } -Controller.prototype.setValue = function(n) { +GUI.Controller.prototype.setValue = function(n) { this.object[this.propertyName] = n; if (this.changeFunction != null) { this.changeFunction.call(this, n); @@ -56,13 +53,13 @@ Controller.prototype.setValue = function(n) { return this; } -Controller.prototype.getValue = function() { +GUI.Controller.prototype.getValue = function() { return this.object[this.propertyName]; } -Controller.prototype.updateDisplay = function() {} +GUI.Controller.prototype.updateDisplay = function() {} -Controller.prototype.onChange = function(fnc) { +GUI.Controller.prototype.onChange = function(fnc) { this.changeFunction = fnc; return this; } diff --git a/controllers/controller.number.js b/controllers/controller.number.js index 3f8e3fb..4c8dcaa 100644 --- a/controllers/controller.number.js +++ b/controllers/controller.number.js @@ -1,8 +1,8 @@ -var NumberController = function() { +GUI.NumberController = function() { this.type = "number"; - Controller.apply(this, arguments); + GUI.Controller.apply(this, arguments); var _this = this; @@ -16,7 +16,22 @@ var NumberController = function() { var min = arguments[3]; var max = arguments[4]; - var step = arguments[5]; + var step = arguments[5]; + + this.step = function(s) { + step = s; + return this; + } + + this.min = function(s) { + min = s; + return this; + } + + this.max = function(s) { + max = s; + return this; + } if (!step) { if (min != undefined && max != undefined) { @@ -38,7 +53,7 @@ var NumberController = function() { var slider; if (min != undefined && max != undefined) { - slider = new Slider(this, min, max, step, this.getValue()); + slider = new GUI.Slider(this, min, max, step, this.getValue()); this.domElement.appendChild(slider.domElement); } @@ -121,7 +136,7 @@ var NumberController = function() { val = max; } - return Controller.prototype.setValue.call(this, val); + return GUI.Controller.prototype.setValue.call(this, val); } @@ -131,5 +146,4 @@ var NumberController = function() { } }; -NumberController.prototype = new Controller(); -NumberController.prototype.constructor = NumberController; +GUI.extendController(GUI.NumberController); diff --git a/controllers/controller.string.js b/controllers/controller.string.js index 57cb832..d29804d 100644 --- a/controllers/controller.string.js +++ b/controllers/controller.string.js @@ -1,9 +1,9 @@ -var StringController = function() { +GUI.StringController = function() { this.type = "string"; var _this = this; - Controller.apply(this, arguments); + GUI.Controller.apply(this, arguments); var input = document.createElement('input'); @@ -30,5 +30,4 @@ var StringController = function() { }; -StringController.prototype = new Controller(); -StringController.prototype.constructor = StringController; +GUI.extendController(GUI.StringController); \ No newline at end of file diff --git a/controllers/scrubber.js b/controllers/scrubber.js new file mode 100644 index 0000000..8ea11cc --- /dev/null +++ b/controllers/scrubber.js @@ -0,0 +1,278 @@ +// Would really love to make it so that as FEW changes as possible are required to gui.js in order to make this work. Would love to make it so you simply include gui.scrubber.min.js in addition to gui.min.js. + +GUI.Controller.prototype.at = function(when, what, tween) { + + this.scrubber.addPoint(new GUI.ScrubberPoint(this.scrubber, when, what)); + this.scrubber.render(); + return this; + +} + +GUI.Scrubber = function(controller, timer) { + + var _this = this; + + var points = []; + this.timer = timer; + this.controller = controller; + this.controller.scrubber = this; + + + this.domElement = document.createElement('div'); + this.domElement.setAttribute('class', 'guidat-time'); + + var canvas = document.createElement('canvas'); + this.domElement.appendChild(canvas); + + this.g = canvas.getContext('2d'); + + var width; + var height; + + this.__defineGetter__("width", function() { + return width; + }); + + this.__defineGetter__("height", function() { + return height; + }); + + controller.domElement.insertBefore(this.domElement, controller.propertyNameElement.nextSibling); + + + this.addPoint = function(point) { + points.push(point); + points.sort(function(a,b) { + return a.time - b.time; + }); + } + + + + this.render = function() { + + // TODO: if visible ... + _this.g.clearRect(0, 0, width, height); + + for (var i in points) { + points[i].render(); + } + + // Draw playhead + + _this.g.strokeStyle = "red"; + var t = Math.round(GUI.map(_this.timer.playhead, _this.timer.windowMin, _this.timer.windowMin+_this.timer.windowWidth, 0, width))+0.5; + _this.g.beginPath(); + _this.g.moveTo(t, 0); + _this.g.lineTo(t, height); + _this.g.stroke(); + + } + + this.render(); + + var onResize = function() { + canvas.width = width = _this.domElement.offsetWidth; + canvas.height = height = _this.domElement.offsetHeight; + _this.render(); + }; + + window.addEventListener('resize', function(e) { + onResize(); + }, false); + + onResize(); + + this.timer.addPlayListener(this.render); + + var onPlayChange = function(curTime, prevTime) { + + // This assumes a SORTED point array + // And a PROGRESSING/INCREASING/GROWING playhead + for (var i = 0; i < points.length; i++) { + var cur = points[i]; + + if (cur.time < prevTime) { + continue; + } + + if (cur.time >= prevTime && cur.time <= curTime) { + pointHandlers[_this.controller.type].call(_this, cur); + } + + } + + }; + + var pointHandlers = { + + 'function': function(point) { + this.controller.getValue().call(this); + }, + + 'boolean': function(point) { + this.controller.setValue(point.value); + }, + + 'string': function(point) { + this.controller.setValue(point.value); + }, + + 'number': function(point) { + // + } + + } + + this.timer.addPlayListener(onPlayChange); + + this.timer.addWindowListener(this.render); + + +}; + +GUI.ScrubberPoint = function(scrubber, time, value) { + + var _this = this; + + var g = scrubber.g; + var timer = scrubber.timer; + this.value = value; + var rectSize = 4; + + this.__defineGetter__("time", function() { + return time; + }); + + this.render = function() { + + var x = GUI.map(time, timer.windowMin, timer.windowMin+timer.windowWidth, 0, 1); + + if (x >= 0 && x <= 1) { + x = Math.round(GUI.map(x, 0, 1, 0, scrubber.width)); + } + + g.save(); + g.translate(x-rectSize/2, 0); + g.fillStyle = "#ffd800"; + g.fillRect(0, 0, rectSize/2, scrubber.height-1); + g.fillStyle = "#ff9000"; + g.fillRect(rectSize/2, 0, rectSize/2, scrubber.height-1); + g.restore(); + + } + +} + +GUI.Timer = function(gui) { + + var _this = this; + + this.gui = gui; + this.gui.timer = this; + + var playhead = 0; + var lastPlayhead = 0; + var playListeners = []; + var windowListeners = []; + + var windowMin = 0; + var windowWidth = 10000; + + var thisTime; + var lastTime; + + var playInterval = -1; + var playResolution = 1000/60; + + var playing = false; + + var millis = function() { + var d = new Date(); + return d.getTime(); + } + + this.__defineGetter__("windowMin", function() { + return windowMin; + }); + + this.__defineSetter__("windowMin", function(v) { + windowMin = v; + for (var i in windowListeners) { + windowListeners[i].call(windowListeners[i]); + } + }); + + this.__defineGetter__("windowWidth", function() { + return windowWidth; + }); + + + this.__defineSetter__("windowWidth", function(v) { + windowWidth = v; + for (var i in windowListeners) { + windowListeners[i].call(windowListeners[i]); + } + }); + + this.__defineGetter__("playhead", function() { + return playhead; + }); + + this.__defineSetter__("playhead", function(t) { + lastPlayhead = playhead; + playhead = t; + for (var i = 0; i < playListeners.length; i++) { + playListeners[i].call(this, playhead, lastPlayhead); + } + }); + + this.__defineGetter__("playing", function() { + return playing; + }); + + this.play = function() { + playing = true; + lastTime = millis(); + if (playInterval == -1) { + playInterval = setInterval(this.update, playResolution); + } + }; + + this.update = function() { + thisTime = millis(); + _this.playhead = _this.playhead + (thisTime - lastTime); + lastTime = thisTime; + }; + + this.pause = function() { + playing = false; + clearInterval(playInterval); + playInterval = -1; + }; + + this.playPause = function() { + if (playing) { + this.pause(); + } else { + this.play(); + } + } + + + this.stop = function() { + this.pause(); + this.playhead = 0; + }; + + this.addPlayListener = function(fnc) { + playListeners.push(fnc); + }; + + this.addWindowListener = function(fnc) { + windowListeners.push(fnc); + }; + + + +} \ No newline at end of file diff --git a/controllers/slider.js b/controllers/slider.js index ee2902e..9c7c28e 100644 --- a/controllers/slider.js +++ b/controllers/slider.js @@ -1,4 +1,4 @@ -var Slider = function(numberController, min, max, step, initValue) { +GUI.Slider = function(numberController, min, max, step, initValue) { var min = min; var max = max; diff --git a/demo/demo.css b/demo/demo.css index 966a9ad..9e5ce3d 100644 --- a/demo/demo.css +++ b/demo/demo.css @@ -66,10 +66,6 @@ div.collapsed h2, div.expanded h2 { width: 100%; cursor: pointer; } -h2.last { -border-bottom: 1px dotted #ccc; -margin-bottom: 20px; -} div.expanded h2:before { content: '-'; @@ -162,10 +158,12 @@ a:active { } footer { +margin-top: 20px; background-color: #eee; width: 510px; padding: 10px; clear: both; + color: #444; } diff --git a/gui-bare.css b/gui-bare.css deleted file mode 100644 index a61a1f2..0000000 --- a/gui-bare.css +++ /dev/null @@ -1,26 +0,0 @@ -#guidat { - position: fixed; - width: 250px; - z-index: 200; - top: 0; - left: 100%; - margin-left: -270px; -} - -#guidat-controllers { - height: 300px; - overflow-y: auto; -} - -#guidat-toggle { - cursor: pointer; -} - -.guidat-controller { - clear: both; -} - -.guidat-controller input { - float: right; - clear: both; -} diff --git a/gui.css b/gui.css index 5ba1d17..db30521 100644 --- a/gui.css +++ b/gui.css @@ -13,14 +13,14 @@ } .guidat-time { -width: 75%; -float: right; -margin-top: -3px; -margin-right: -2px; -margin-left: 3px; -background-color: #333; -border-bottom: 1px solid #444; -height: 31px; + width: 75%; + float: right; + margin-top: -3px; + margin-right: -2px; + margin-left: 3px; + background-color: #333; + border-bottom: 1px solid #444; + height: 31px; } .guidat { @@ -47,6 +47,11 @@ height: 31px; */ } +.guidat-controllers hr { +height: 0; +border-top: 1px solid #000; +} + a.guidat-toggle { text-decoration: none; cursor: pointer; diff --git a/gui.js b/gui.js index cb9cffd..a8b4c93 100644 --- a/gui.js +++ b/gui.js @@ -17,9 +17,6 @@ var GUI = function() { var curControllerContainerHeight = 0; - // How big we get when we open - - var _this = this; var open = false; @@ -27,6 +24,8 @@ var GUI = function() { // Prevents checkForOverflow bug in which loaded gui appearance // settings are not respected by presence of scrollbar. var explicitOpenHeight = false; + + // How big we get when we open var openHeight; var name; @@ -41,7 +40,10 @@ var GUI = function() { var controllerContainer = document.createElement('div'); controllerContainer.setAttribute('class', 'guidat-controllers'); - // Firefox hack for horizontal scrolling + // For use with GUIScrubber + this.timer = null; + + // Firefox hack to prevent horizontal scrolling controllerContainer.addEventListener('DOMMouseScroll', function(e) { var scrollAmount = this.scrollTop; @@ -80,19 +82,17 @@ var GUI = function() { my = e.pageY; mx = e.pageX; - var dmy = my - pmy; - - - if (!open) { - if (dmy > 0) { - open = true; - curControllerContainerHeight = openHeight = 1; - toggleButton.innerHTML = name || "Hide Controls"; - } else { - return; + var dmy = pmy-my; + + if (!open) { + if (dmy < 0) { + open = true; + curControllerContainerHeight = openHeight = 1; + toggleButton.innerHTML = name || "Hide Controls"; + } else { + return; + } } - } - // TODO: Flip this if you want to resize to the left. var dmx = pmx - mx; @@ -107,7 +107,7 @@ var GUI = function() { dragDisplacementY += dmy; dragDisplacementX += dmx; openHeight += dmy; - width += dmx; + //width += dmx; curControllerContainerHeight += dmy; controllerContainer.style.height = openHeight+'px'; width = GUI.constrain(width, MIN_WIDTH, MAX_WIDTH); @@ -141,9 +141,9 @@ var GUI = function() { _this.toggle(); // Clears lingering slider column - _this.domElement.style.width = (width+1)+'px'; + // _this.domElement.style.width = (width+1)+'px'; setTimeout(function() { - _this.domElement.style.width = width+'px'; + // _this.domElement.style.width = width+'px'; }, 1); } @@ -194,8 +194,8 @@ var GUI = function() { }, false); - this.domElement.appendChild(controllerContainer); this.domElement.appendChild(toggleButton); + this.domElement.appendChild(controllerContainer); if (GUI.autoPlace) { if(GUI.autoPlaceContainer == null) { @@ -259,13 +259,6 @@ var GUI = function() { this.autoListen = true; - var handlerTypes = { - "number": NumberController, - "string": StringController, - "boolean": BooleanController, - "function": FunctionController - }; - var alreadyControlled = function(object, propertyName) { for (var i in controllers) { if (controllers[i].object == object && @@ -284,10 +277,22 @@ var GUI = function() { F.prototype = constructor.prototype; return new F(); }; + + this.divider = function() { + controllerContainer.appendChild(document.createElement('hr')); + } this.add = function() { - + var object = arguments[0]; + + if (arguments.length == 1) { + for (var i in object) { + this.add(object, i); + } + return; + } + var propertyName = arguments[1]; // Have we already added this? @@ -347,6 +352,10 @@ var GUI = function() { openHeight = controllerHeight; } + if (this.timer != null) { + new GUI.Scrubber(controllerObject, this.timer); + } + return controllerObject; } @@ -361,14 +370,13 @@ var GUI = function() { } else { controllerContainer.style.overflowY = "hidden"; } - // console.log(controllerHeight, openHeight); } - var addHandlers = { - "number": NumberController, - "string": StringController, - "boolean": BooleanController, - "function": FunctionController + var handlerTypes = { + "number": GUI.NumberController, + "string": GUI.StringController, + "boolean": GUI.BooleanController, + "function": GUI.FunctionController }; var alreadyControlled = function(object, propertyName) { @@ -390,7 +398,7 @@ var GUI = function() { }; this.reset = function() { - // + // TODO } // GUI ... GUI @@ -485,10 +493,8 @@ GUI.saveURL = function() { GUI.scrollTop = -1; -// TODO: Not working in FF. GUI.load = function(saveString) { - //GUI.savedAppearanceVars = []; var vals = saveString.split(","); var numGuis = parseInt(vals[0]); @@ -499,6 +505,7 @@ GUI.load = function(saveString) { } GUI.savedValues = vals.splice(2, vals.length); + }; GUI.savedValues = []; @@ -616,7 +623,7 @@ GUI.constrain = function (v, o1, o2) { GUI.error = function(str) { if (typeof console.error == 'function') { - console.GUI.error("[GUI ERROR] " + str); + console.error("[GUI ERROR] " + str); } }; @@ -625,4 +632,9 @@ GUI.roundToDecimal = function(n, decimals) { return Math.round(n*t)/t; } +GUI.extendController = function(clazz) { + clazz.prototype = new GUI.Controller(); + clazz.prototype.constructor = clazz; +} + if (GUI.getVarFromURL('saveString') != null) GUI.load(GUI.getVarFromURL('saveString')); \ No newline at end of file diff --git a/index.html b/index.html index 783b71f..c115756 100644 --- a/index.html +++ b/index.html @@ -3,8 +3,6 @@
- -gui-dat is a lightweight controller library for JavaScript. It allows you to easily manipulate variables and fire functions on the fly.
- --<script type="text/javascript" src="gui.min.js"></script> -<script type="text/javascript"> - -window.onload = function() { - - var fizzyText = new FizzyText("gui-dat"); - - var gui = new GUI(); - - // Text field - gui.add(fizzyText, "message"); - - // Sliders with min and max - gui.add(fizzyText, "maxSize", 0.5, 7); - gui.add(fizzyText, "growthSpeed", 0.01, 1); - gui.add(fizzyText, "speed", 0.1, 2); - - // Sliders with min, max and increment. - gui.add(fizzyText, "noiseStrength", 10, 100, 5); - - // Boolean checkbox - gui.add(fizzyText, "displayOutline"); - - // Fires a function called "explode" - gui.add(fizzyText, "explode").name("Explode!"); // Specify a custom name. - -}; - -</script> -- -
this.prop = value
.The simplest way to save your parameters is via GUI.saveURL()
. This method directs your browser to a URL containing the current GUI settings.
-// Make a button for the url function -gui.add(GUI, "saveURL");- -
Let's say you'd like to share your settings with someone. Instead of sending a long link with lots of parameters stored in it, you can make your saved settings the defaults.
- -First, add the method GUI.showSaveString()
to a gui object:
var gui = new GUI(); - -// Add some stuff (and pretend I change their values); -gui.add(someObject, "someProperty"); -gui.add(someObject, "someOtherProperty"); - -// Make a save button. -gui.add(GUI, "showSaveString");- -
Clicking the "showSaveString" button bring up an alert with a string. Copy and paste that string into the method GUI.load()
before you instantiate any gui objects.
-// Replace COPIED STRING with the value you got from showSaveString() -GUI.load("COPIED STRING"); - -var gui = new GUI(); - -// Now these properties will be set to the values you just saved. -gui.add(someObject, "someProperty"); -gui.add(someObject, "someOtherProperty");- -
Save strings won't work if you change the order in which you've added properties to your gui objects, or the order of the gui objects themselves.. If you want to add more parameters to your gui and use an old save string, make sure they're added after the properties whose values you've saved.
-Let's say you have a variable that changes by itself from time to time. If you'd like the GUI to reflect those changes, use the listen()
method.
gui.add(obj, "changingProperty").listen();-
By default, gui-dat will create an internal interval that checks for changes in the values you've marked with listen()
. If you'd like to check for these changes in an interval of your own definition, use the following:
-gui.autoListen = false; // disables internal interval -gui.add(obj, "changingProperty").listen(); - -// Make your own loop -setInterval(function() { - gui.listen(); // updates values you've marked with listen() -}, 1000 / 60);- -
Alternatively, you can forego calling listen()
on individual controllers, and instead choose to monitor changes in all values controlled by your gui.
-gui.autoListen = false; // disables internal interval -gui.add(obj, "add"); -gui.add(obj, "lotsa"); -gui.add(obj, "properties"); - -// Make your own loop -setInterval(function() { - gui.listenAll(); // updates ALL values managed by this gui -}, 1000 / 60);-
You can instantiate multiple GUI
objects and name them however you'd like.
var gui1 = new GUI(); -var gui2 = new GUI(); - -// The name function overwrites the "Show Controls" text. -gui1.name("Utilities"); -gui2.name("Camera Placement");- -
By default, gui-dat panels will be automatically added to the HTML document and fixed to the top of the screen. You can disable this behavior / styling and append the gui DOM element to a container of your choosing.
--// Notice this belongs to the GUI class (uppercase) -// and not an instance thereof. -GUI.autoPlace = false; - -var gui = new GUI(); - -// Do some custom styles ... -gui.domElement.style.position = "absolute"; -gui.domElement.style.top = "20px"; -gui.domElement.style.left = "20px"; - -document.getElementById("my-gui-container").appendChild( gui.domElement );-