diff --git a/examples/Animated/Flock.html b/examples/Animated/Flock.html index 63c1cc17..1c217eea 100644 --- a/examples/Animated/Flock.html +++ b/examples/Animated/Flock.html @@ -287,7 +287,7 @@ var layer = document.activeLayer; function onKeyDown(event) { - if (event.keyCode == 'space') + if (event.key == 'space') layer.selected = !layer.selected; } diff --git a/src/item/Item.js b/src/item/Item.js index 9029eee6..7ca7feaf 100644 --- a/src/item/Item.js +++ b/src/item/Item.js @@ -937,7 +937,7 @@ var Item = this.Item = Base.extend({ if (!func || !func._installed) { var hash = {}; hash[handler] = function(event) { - // Always clear the drag set on mouse-up + // Always clear the drag set on mouseup if (name === 'up') sets.drag = {}; removeAll(sets[name]); diff --git a/src/paper.js b/src/paper.js index 18332d96..182d8029 100644 --- a/src/paper.js +++ b/src/paper.js @@ -59,11 +59,7 @@ Base.inject({ var start = start || 0, length = length || list.length - start; var obj = list[start]; - // As a convention, do not return objects that are owned, e.g. - // LinkedPoint or SegmentPoint, although they are instances of Point, - // since they override properties with beans. Convert these to pure - // Points instead, further down. - if (obj && !obj._owner && obj instanceof this + if (obj instanceof this // If the class defines _readNull, return null when nothing // was provided || this.prototype._readNull && obj == null && length <= 1) @@ -122,8 +118,28 @@ Base.inject({ }); }, + camelize: function(str) { + return str.replace(/-(\w)/g, function(all, chr) { + return chr.toUpperCase(); + }); + }, + + /** + * Utility function for rendering numbers to strings at a precision of up + * to 5 fractional digits. + */ formatNumber: function(num) { return (Math.round(num * 100000) / 100000).toString(); + }, + + /** + * Utility function for rendering objects to strings, in object literal + * notation. + */ + formatObject: function(obj) { + return '{ ' + Base.each(obj, function(value, key) { + this.push(key + ': ' + value); + }, []).join(', ') + ' }'; } }); diff --git a/src/tool/Tool.js b/src/tool/Tool.js index 8d789df0..0717fccd 100644 --- a/src/tool/Tool.js +++ b/src/tool/Tool.js @@ -31,7 +31,7 @@ var Tool = this.Tool = ToolHandler.extend(new function() { this.events = { mousedown: function(event) { curPoint = viewToArtwork(event, that._document); - that.onHandleEvent('mouse-down', curPoint, event); + that.onHandleEvent('mousedown', curPoint, event); if (that.onMouseDown) that._document.redraw(); if (that.eventInterval != null) { @@ -54,9 +54,9 @@ var Tool = this.Tool = ToolHandler.extend(new function() { if (dragging && !onlyMove) { curPoint = point || curPoint; if (curPoint) - that.onHandleEvent('mouse-drag', curPoint, event); + that.onHandleEvent('mousedrag', curPoint, event); } else if (!dragging || onlyMove) { - that.onHandleEvent('mouse-move', point, event); + that.onHandleEvent('mousemove', point, event); } if (that.onMouseMove || that.onMouseDrag) that._document.redraw(); @@ -67,7 +67,7 @@ var Tool = this.Tool = ToolHandler.extend(new function() { curPoint = null; if (that.eventInterval != null) clearInterval(this.timer); - that.onHandleEvent('mouse-up', + that.onHandleEvent('mouseup', viewToArtwork(event, that._document), event); if (that.onMouseUp) that._document.redraw(); diff --git a/src/tool/ToolEvent.js b/src/tool/ToolEvent.js index 5a0df5d1..b68b1fec 100644 --- a/src/tool/ToolEvent.js +++ b/src/tool/ToolEvent.js @@ -39,25 +39,13 @@ var ToolEvent = this.ToolEvent = Base.extend({ this.type = type; this.event = event; }, - - toString: function() { - return '{ type: ' + this.type - + ', point: ' + this.point - + ', count: ' + this.count - + ', modifiers: ' + this.modifiers - + ' }'; - }, /** * Convenience method to allow local overrides of point values. * See application below. */ - choosePoint: function(point, toolPoint) { - if (point) - return point; - if (toolPoint) - return new Point(toolPoint); - return null; + _choosePoint: function(point, toolPoint) { + return point ? point : toolPoint ? toolPoint.clone() : null; }, /** @@ -78,7 +66,7 @@ var ToolEvent = this.ToolEvent = Base.extend({ * */ getPoint: function() { - return this.choosePoint(this._point, this.tool.point); + return this._choosePoint(this._point, this.tool.point); }, setPoint: function(point) { @@ -90,7 +78,7 @@ var ToolEvent = this.ToolEvent = Base.extend({ * event was fired. */ getLastPoint: function() { - return this.choosePoint(this._lastPoint, this.tool.lastPoint); + return this._choosePoint(this._lastPoint, this.tool.lastPoint); }, setLastPoint: function(lastPoint) { @@ -102,7 +90,7 @@ var ToolEvent = this.ToolEvent = Base.extend({ * was last clicked. */ getDownPoint: function() { - return this.choosePoint(this._downPoint, this.tool.downPoint); + return this._choosePoint(this._downPoint, this.tool.downPoint); }, setDownPoint: function(downPoint) { @@ -117,7 +105,7 @@ var ToolEvent = this.ToolEvent = Base.extend({ */ getMiddlePoint: function() { // For explanations, see getDelta() - if (this._middlePoint == null && this.tool.lastPoint != null) { + if (!this._middlePoint && this.tool.lastPoint) { // (point + lastPoint) / 2 return this.tool.point.add(this.tool.lastPoint).divide(2); } @@ -130,8 +118,8 @@ var ToolEvent = this.ToolEvent = Base.extend({ /** * The difference between the current position and the last position of the - * mouse when the event was fired. In case of the mouse-up event, the - * difference to the mouse-down position is returned. + * mouse when the event was fired. In case of the mouseup event, the + * difference to the mousedown position is returned. */ getDelta: function() { // Do not put the calculated delta into delta, since this only reserved @@ -139,10 +127,9 @@ var ToolEvent = this.ToolEvent = Base.extend({ // Instead, keep calculating the delta each time, so the result can be // directly modified by the script without changing the internal values. // We could cache this and use clone, but this is almost as fast... - if (this._delta == null && this.tool.lastPoint != null) { - return this.tool.point.subtract(this.tool.lastPoint); - } - return this._delta; + return this._delta && this.tool.lastPoint + ? this.tool.point.subtract(this.tool.lastPoint) + : this._delta; }, setDelta: function(delta) { @@ -168,32 +155,21 @@ var ToolEvent = this.ToolEvent = Base.extend({ * */ getCount: function() { - switch (this.type) { - case 'mouse-down': - case 'mouse-up': - // Return downCount for both mouse down and up, since - // the count is the same. - return this.tool.downCount; - default: - return this.tool.count; - } + // Return downCount for both mouse down and up, since + // the count is the same. + return /^mouse(down|up)$/.test(this.type) + ? this.tool.downCount + : this.tool.count; }, setCount: function(count) { - switch (this.type) { - case 'mouse-down': - case 'mouse-up': - this.tool.downCount = count; - break; - default: - this.tool.count = count; - break; - } + this.tool[/^mouse(down|up)$/.test(this.type) ? 'downCount' : 'count'] + = count; }, getModifiers: function() { return Key.modifiers; - } + }, // TODO: implement hitTest first // getItem: function() { @@ -215,4 +191,12 @@ var ToolEvent = this.ToolEvent = Base.extend({ // setItem: function(Item item) { // this.item = item; // } + + toString: function() { + return '{ type: ' + this.type + + ', point: ' + this.getPoint() + + ', count: ' + this.getCount() + + ', modifiers: ' + this.getModifiers() + + ' }'; + } }); diff --git a/src/tool/ToolHandler.js b/src/tool/ToolHandler.js index 87faec5f..8c164974 100644 --- a/src/tool/ToolHandler.js +++ b/src/tool/ToolHandler.js @@ -35,8 +35,8 @@ var ToolHandler = this.ToolHandler = Base.extend({ * * Sample code: * - * // Fire the onMouseDrag event after the user has dragged - * // more then 5 points from the last onMouseDrag event: + * // Fire the onMouseDrag event after the user has dragged more then 5 + * // points from the last onMouseDrag event: * tool.minDistance = 5; * */ @@ -98,42 +98,38 @@ var ToolHandler = this.ToolHandler = Base.extend({ this.lastPoint = this.point; this.point = pt; switch (type) { - case 'mouse-down': + case 'mousedown': this.lastPoint = this.downPoint; this.downPoint = this.point; this.downCount++; break; - case 'mouse-up': - // Mouse up events return the down point for last point, - // so delta is spanning over the whole drag. + case 'mouseup': + // Mouse up events return the down point for last point, so delta is + // spanning over the whole drag. this.lastPoint = this.downPoint; break; } - if (start) { - this.count = 0; - } else { - this.count++; - } + this.count = start ? 0 : this.count + 1; return true; }, onHandleEvent: function(type, pt, event) { switch (type) { - case 'mouse-down': + case 'mousedown': this.updateEvent(type, pt, null, null, true, false, false); if (this.onMouseDown) this.onMouseDown(new ToolEvent(this, type, event)); break; - case 'mouse-drag': - // In order for idleInterval drag events to work, we need to - // not check the first call for a change of position. - // Subsequent calls required by min/maxDistance functionality - // will require it, otherwise this might loop endlessly. + case 'mousedrag': + // In order for idleInterval drag events to work, we need to not + // check the first call for a change of position. Subsequent calls + // required by min/maxDistance functionality will require it, + // otherwise this might loop endlessly. var needsChange = false, - // If the mouse is moving faster than maxDistance, do not - // produce events for what is left after the first event is - // generated in case it is shorter than maxDistance, as this - // would produce weird results. matchMaxDistance controls this. + // If the mouse is moving faster than maxDistance, do not produce + // events for what is left after the first event is generated in + // case it is shorter than maxDistance, as this would produce weird + // results. matchMaxDistance controls this. matchMaxDistance = false; while (this.updateEvent(type, pt, this.minDistance, this.maxDistance, false, needsChange, matchMaxDistance)) { @@ -143,11 +139,11 @@ var ToolHandler = this.ToolHandler = Base.extend({ matchMaxDistance = true; } break; - case 'mouse-up': - // If the last mouse drag happened in a different place, call - // mouse drag first, then mouse up. + case 'mouseup': + // If the last mouse drag happened in a different place, call mouse + // drag first, then mouse up. if ((this.point.x != pt.x || this.point.y != pt.y) - && this.updateEvent('mouse-drag', pt, this.minDistance, + && this.updateEvent('mousedrag', pt, this.minDistance, this.maxDistance, false, false, false)) { if (this.onMouseDrag) this.onMouseDrag(new ToolEvent(this, type, event)); @@ -156,11 +152,11 @@ var ToolHandler = this.ToolHandler = Base.extend({ false, false); if (this.onMouseUp) this.onMouseUp(new ToolEvent(this, type, event)); - // Start with new values for 'mouse-move' + // Start with new values for 'mousemove' this.updateEvent(type, pt, null, null, true, false, false); this.firstMove = true; break; - case 'mouse-move': + case 'mousemove': while (this.updateEvent(type, pt, this.minDistance, this.maxDistance, this.firstMove, true, false)) { if (this.onMouseMove) diff --git a/src/ui/Event.js b/src/ui/Event.js index 8a7d0919..f05b6242 100644 --- a/src/ui/Event.js +++ b/src/ui/Event.js @@ -15,6 +15,8 @@ */ var Event = this.Event = Base.extend({ + beans: true, + initialize: function(event) { this.event = event; }, diff --git a/src/ui/Key.js b/src/ui/Key.js index 1342a8d7..6e2b2a08 100644 --- a/src/ui/Key.js +++ b/src/ui/Key.js @@ -16,18 +16,21 @@ var Key = this.Key = new function() { // TODO: make sure the keys are called the same as in Scriptographer - // Missing: tab, cancel, clear, pause, page-down, page-up, end, home, comma, - // minus, period, slash, etc etc etc. + // Missing: tab, cancel, clear, page-down, page-up, comma, minus, period, + // slash, etc etc etc. var keys = { 8: 'backspace', 13: 'enter', 16: 'shift', 17: 'control', - 19: 'option', // was alt - 20: 'capsLock', + 18: 'option', + 19: 'pause', + 20: 'caps-lock', 27: 'escape', 32: 'space', + 35: 'end', + 36: 'home', 37: 'left', 38: 'up', 39: 'right', @@ -41,57 +44,84 @@ var Key = this.Key = new function() { control: false, option: false, command: false, - capsLock: false + capsLock: false, + + toString: function() { + return Base.formatObject(this); + } }, - keyCodes = {}, - downCode, - downTimer; + // Since only keypress gets proper keyCodes that are actually representing + // characters, we need to perform a little trickery here to use these codes + // in onKeyDown/Up: keydown is used to store the downCode and handle + // modifiers and special keys such as arrows, space, etc, keypress fires the + // actual onKeyDown event and maps the keydown keyCode to the keypress + // charCode so keyup can do the right thing too. + charCodeMap = {}, // keyCode -> charCode mappings for pressed keys + keyMap = {}, // Map for currently pressed keys + downCode; // The last keyCode from keydown - function handleKey(down, code, event) { - var character = String.fromCharCode(code), - keyCode = keys[code] || character.toLowerCase(), + function handleKey(down, keyCode, charCode, event) { + var character = String.fromCharCode(charCode), + key = keys[keyCode] || character.toLowerCase(), handler = down ? 'onKeyDown' : 'onKeyUp'; - console.log(handler, keyCode, character); - if (modifiers[keyCode] !== undefined) { - modifiers[keyCode] = down; - } else if (paper.tool && paper.tool[handler]) { + keyMap[key] = down; + if (paper.tool && paper.tool[handler]) { // Call the onKeyDown or onKeyUp handler if present // When the handler function returns false, prevent the // default behaviour of the key event: // PORT: Add to Sg - var keyEvent = new KeyEvent(down, keyCode, character, event); + var keyEvent = new KeyEvent(down, key, character, event); if (paper.tool[handler](keyEvent) === false) { keyEvent.preventDefault(); } } } - // Since only keypress gest proper keyCodes that are actually representing - // characters, we need to add a little timeout to keydown events to see if - // they are follow immediately by a keypress, and if so, map the keyCode - // from the keydown to the one from keypress, so keyup still knows what - // code has now been released. DomEvent.add(document, { keydown: function(event) { - var code = downCode = event.which || event.keyCode; - downTimer = setTimeout(function() { - keyCodes[code] = code; - handleKey(true, code, event); - }, 1); + var code = event.which || event.keyCode; + // If the keyCode is in keys, it needs to be handled by keydown and + // not in keypress after (arrows for example wont be triggering + // a keypress, but space would). + var key = keys[code], name; + if (key) { + // Do not fire handleKey for modifiers, but for other keys such + // ass arrows, delete, backspace, etc. + if (modifiers[name = Base.camelize(key)] !== undefined) { + modifiers[name] = true; + } else { + // No char code for special keys, but mark as pressed + charCodeMap[code] = 0; + handleKey(true, code, null, event); + } + // Do not set downCode as we handled it already. Space would + // be handled twice otherwise, once here, once in keypress. + } else { + downCode = code; + } }, keypress: function(event) { - clearTimeout(downTimer); - var code = event.which || event.keyCode; - keyCodes[downCode] = code; - handleKey(true, code, event); + if (downCode != null) { + var code = event.which || event.keyCode; + // Link the downCode from keydown with the code form keypress, so + // keyup can retrieve that code again. + charCodeMap[downCode] = code; + handleKey(true, downCode, code, event); + downCode = null; + } }, keyup: function(event) { - var code = event.which || event.keyCode; - handleKey(false, keyCodes[code], event); - delete keyCodes[code]; + var code = event.which || event.keyCode, + key = keys[code], name; + if (key && modifiers[name = Base.camelize(key)] !== undefined) { + modifiers[name] = false + } else if (charCodeMap[code] != null) { + handleKey(false, code, charCodeMap[code], event); + delete charCodeMap[code]; + } } }); @@ -99,7 +129,7 @@ var Key = this.Key = new function() { modifiers: modifiers, isDown: function(key) { - return !!activeKeys[key]; + return !!keyMap[key]; } }; }; \ No newline at end of file diff --git a/src/ui/KeyEvent.js b/src/ui/KeyEvent.js index 3ea14f7c..d63eb315 100644 --- a/src/ui/KeyEvent.js +++ b/src/ui/KeyEvent.js @@ -16,11 +16,19 @@ var KeyEvent = this.KeyEvent = Event.extend(new function() { return { - initialize: function(down, keyCode, character, event) { + initialize: function(down, key, character, event) { this.base(event); - this.type = down ? 'key-down' : 'key-up'; - this.keyCode = keyCode; + this.type = down ? 'keydown' : 'keyup'; + this.key = key; this.character = character; + }, + + toString: function() { + return '{ type: ' + this.type + + ', key: ' + this.key + + ', character: ' + this.character + + ', modifiers: ' + this.getModifiers() + + ' }'; } }; }); diff --git a/src/util/PaperScript.js b/src/util/PaperScript.js index 4c7aefdc..121d0bd5 100644 --- a/src/util/PaperScript.js +++ b/src/util/PaperScript.js @@ -134,14 +134,13 @@ var PaperScript = this.PaperScript = new function() { var doc = paper.document, tool = paper.tool = /on(?:Key|Mouse)(?:Up|Down|Move|Drag)/.test(code) && new Tool(null, doc), - onEditOptions, onOptions, onSelect, onDeselect, onReselect, - onMouseDown, onMouseUp, onMouseDrag, onMouseMove, onKeyDown, - onKeyUp, + onEditOptions, onSelect, onDeselect, onReselect, onMouseDown, + onMouseUp, onMouseDrag, onMouseMove, onKeyDown, onKeyUp, res = eval(compile(code)); if (tool) { - Base.each(['onEditOptions', 'onOptions', 'onSelect', - 'onDeselect', 'onReselect', 'onMouseDown', 'onMouseUp', - 'onMouseDrag', 'onMouseMove', 'onKeyDown', 'onKeyUp'], + Base.each(['onEditOptions', 'onSelect', 'onDeselect', + 'onReselect', 'onMouseDown', 'onMouseUp', 'onMouseDrag', + 'onMouseMove', 'onKeyDown', 'onKeyUp'], function(key) { tool[key] = eval(key); }