From d1642c0729eb5e08d1ff5b53e7af513942a6fb7a Mon Sep 17 00:00:00 2001 From: Lorenzo Polidori Date: Fri, 25 Oct 2013 23:15:42 +0100 Subject: [PATCH] Added object controls visibility (github issue #552). --- src/mixins/object_interactivity.mixin.js | 200 +++++++++++++++++------ test.js | 3 +- test/unit/object_interactivity.js | 87 ++++++++++ 3 files changed, 235 insertions(+), 55 deletions(-) create mode 100644 test/unit/object_interactivity.js diff --git a/src/mixins/object_interactivity.mixin.js b/src/mixins/object_interactivity.mixin.js index 2b9bd95b..f5b37864 100644 --- a/src/mixins/object_interactivity.mixin.js +++ b/src/mixins/object_interactivity.mixin.js @@ -6,7 +6,13 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prototype */ { /** - * Determines which one of the four corners has been clicked + * The object interactivity controls. + * @private + */ + _controlsVisibility: null, + + /** + * Determines which corner has been clicked * @private * @param {Event} e Event object * @param {Object} offset Canvas offset @@ -23,6 +29,10 @@ for (var i in this.oCoords) { + if (!this.isControlVisible(i)) { + continue; + } + if (i === 'mtr' && !this.hasRotatingPoint) { continue; } @@ -279,7 +289,7 @@ ~~(h + padding2 + strokeWidth * this.scaleY) + 1 ); - if (this.hasRotatingPoint && !this.get('lockRotation') && this.hasControls) { + if (this.hasRotatingPoint && this.isControlVisible('mtr') && !this.get('lockRotation') && this.hasControls) { var rotateHeight = ( this.flipY @@ -338,65 +348,80 @@ ctx.strokeStyle = ctx.fillStyle = this.cornerColor; // top-left - _left = left - scaleOffsetX - strokeWidth2 - paddingX; - _top = top - scaleOffsetY - strokeWidth2 - paddingY; - - isVML || transparent || ctx.clearRect(_left, _top, sizeX, sizeY); - ctx[methodName](_left, _top, sizeX, sizeY); - - // top-right - _left = left + width - scaleOffsetX + strokeWidth2 + paddingX; - _top = top - scaleOffsetY - strokeWidth2 - paddingY; - - isVML || transparent || ctx.clearRect(_left, _top, sizeX, sizeY); - ctx[methodName](_left, _top, sizeX, sizeY); - - // bottom-left - _left = left - scaleOffsetX - strokeWidth2 - paddingX; - _top = top + height + scaleOffsetSizeY + strokeWidth2 + paddingY; - - isVML || transparent || ctx.clearRect(_left, _top, sizeX, sizeY); - ctx[methodName](_left, _top, sizeX, sizeY); - - // bottom-right - _left = left + width + scaleOffsetSizeX + strokeWidth2 + paddingX; - _top = top + height + scaleOffsetSizeY + strokeWidth2 + paddingY; - - isVML || transparent || ctx.clearRect(_left, _top, sizeX, sizeY); - ctx[methodName](_left, _top, sizeX, sizeY); - - if (!this.get('lockUniScaling')) { - // middle-top - _left = left + width/2 - scaleOffsetX; - _top = top - scaleOffsetY - strokeWidth2 - paddingY; - - isVML || transparent || ctx.clearRect(_left, _top, sizeX, sizeY); - ctx[methodName](_left, _top, sizeX, sizeY); - - // middle-bottom - _left = left + width/2 - scaleOffsetX; - _top = top + height + scaleOffsetSizeY + strokeWidth2 + paddingY; - - isVML || transparent || ctx.clearRect(_left, _top, sizeX, sizeY); - ctx[methodName](_left, _top, sizeX, sizeY); - - // middle-right - _left = left + width + scaleOffsetSizeX + strokeWidth2 + paddingX; - _top = top + height/2 - scaleOffsetY; - - isVML || transparent || ctx.clearRect(_left, _top, sizeX, sizeY); - ctx[methodName](_left, _top, sizeX, sizeY); - - // middle-left + if (this.isControlVisible('tl')) { _left = left - scaleOffsetX - strokeWidth2 - paddingX; - _top = top + height/2 - scaleOffsetY; + _top = top - scaleOffsetY - strokeWidth2 - paddingY; isVML || transparent || ctx.clearRect(_left, _top, sizeX, sizeY); ctx[methodName](_left, _top, sizeX, sizeY); } + // top-right + if (this.isControlVisible('tr')) { + _left = left + width - scaleOffsetX + strokeWidth2 + paddingX; + _top = top - scaleOffsetY - strokeWidth2 - paddingY; + + isVML || transparent || ctx.clearRect(_left, _top, sizeX, sizeY); + ctx[methodName](_left, _top, sizeX, sizeY); + } + + // bottom-left + if (this.isControlVisible('bl')) { + _left = left - scaleOffsetX - strokeWidth2 - paddingX; + _top = top + height + scaleOffsetSizeY + strokeWidth2 + paddingY; + + isVML || transparent || ctx.clearRect(_left, _top, sizeX, sizeY); + ctx[methodName](_left, _top, sizeX, sizeY); + } + + // bottom-right + if (this.isControlVisible('br')) { + _left = left + width + scaleOffsetSizeX + strokeWidth2 + paddingX; + _top = top + height + scaleOffsetSizeY + strokeWidth2 + paddingY; + + isVML || transparent || ctx.clearRect(_left, _top, sizeX, sizeY); + ctx[methodName](_left, _top, sizeX, sizeY); + } + + if (!this.get('lockUniScaling')) { + // middle-top + if (this.isControlVisible('mt')) { + _left = left + width/2 - scaleOffsetX; + _top = top - scaleOffsetY - strokeWidth2 - paddingY; + + isVML || transparent || ctx.clearRect(_left, _top, sizeX, sizeY); + ctx[methodName](_left, _top, sizeX, sizeY); + } + + // middle-bottom + if (this.isControlVisible('mb')) { + _left = left + width/2 - scaleOffsetX; + _top = top + height + scaleOffsetSizeY + strokeWidth2 + paddingY; + + isVML || transparent || ctx.clearRect(_left, _top, sizeX, sizeY); + ctx[methodName](_left, _top, sizeX, sizeY); + } + + // middle-right + if (this.isControlVisible('mr')) { + _left = left + width + scaleOffsetSizeX + strokeWidth2 + paddingX; + _top = top + height/2 - scaleOffsetY; + + isVML || transparent || ctx.clearRect(_left, _top, sizeX, sizeY); + ctx[methodName](_left, _top, sizeX, sizeY); + } + // middle-left + if (this.isControlVisible('ml')) { + _left = left - scaleOffsetX - strokeWidth2 - paddingX; + _top = top + height/2 - scaleOffsetY; + + isVML || transparent || ctx.clearRect(_left, _top, sizeX, sizeY); + ctx[methodName](_left, _top, sizeX, sizeY); + } + } + // middle-top-rotate - if (this.hasRotatingPoint) { + if (this.hasRotatingPoint && this.isControlVisible('mtr')) { _left = left + width/2 - scaleOffsetX; _top = this.flipY ? @@ -410,6 +435,73 @@ ctx.restore(); return this; + }, + + /** + * Returns true if the specified control is visible, false otherwise. + * @param {String} controlName The name of the control. Possible values are 'tl', 'tr', 'br', 'bl', 'ml', 'mt', 'mr', 'mb', 'mtr'. + * @returns {Boolean} true if the specified control is visible, false otherwise + */ + isControlVisible: function(controlName) { + return this._getControlsVisibility()[controlName]; + }, + + /** + * Sets the visibility of the specified control. + * @param {String} controlName The name of the control. Possible values are 'tl', 'tr', 'br', 'bl', 'ml', 'mt', 'mr', 'mb', 'mtr'. + * @param {Boolean} visible true to set the specified control visible, false otherwise + * @return {fabric.Object} thisArg + * @chainable + */ + setControlVisible: function(controlName, visible) { + this._getControlsVisibility()[controlName] = visible; + return this; + }, + + /** + * Sets the visibility state of object controls. + * @param {Object} [options] Options object + * @param {Boolean} [options.bl] true to enable the bottom-left control, false to disable it + * @param {Boolean} [options.br] true to enable the bottom-right control, false to disable it + * @param {Boolean} [options.mb] true to enable the middle-bottom control, false to disable it + * @param {Boolean} [options.ml] true to enable the middle-left control, false to disable it + * @param {Boolean} [options.mr] true to enable the middle-right control, false to disable it + * @param {Boolean} [options.mt] true to enable the middle-top control, false to disable it + * @param {Boolean} [options.tl] true to enable the top-left control, false to disable it + * @param {Boolean} [options.tr] true to enable the top-right control, false to disable it + * @param {Boolean} [options.mtr] true to enable the middle-top-rotate control, false to disable it + * @return {fabric.Object} thisArg + * @chainable + */ + setControlsVisibility: function(options) { + options || (options = { }); + + for (var p in options) { + this.setControlVisible(p, options[p]); + } + return this; + }, + + /** + * Returns the instance of the control visibility set for this object. + * @private + * @returns {Object} + */ + _getControlsVisibility: function() { + if (!this._controlsVisibility) { + this._controlsVisibility = { + tl: true, + tr: true, + br: true, + bl: true, + ml: true, + mt: true, + mr: true, + mb: true, + mtr: true + }; + } + return this._controlsVisibility; } }); })(); diff --git a/test.js b/test.js index 54a59172..9cd4aa65 100644 --- a/test.js +++ b/test.js @@ -29,7 +29,8 @@ testrunner.run({ './test/unit/canvas_static.js', './test/unit/gradient.js', './test/unit/pattern.js', - './test/unit/shadow.js' + './test/unit/shadow.js', + './test/unit/object_interactivity.js' ] }, function(err, report) { if (err) { diff --git a/test/unit/object_interactivity.js b/test/unit/object_interactivity.js new file mode 100644 index 00000000..8a0d67f3 --- /dev/null +++ b/test/unit/object_interactivity.js @@ -0,0 +1,87 @@ +(function() { + + QUnit.module('fabric.ObjectInteractivity'); + + test('isControlVisible', function(){ + ok(fabric.Object); + + var cObj = new fabric.Object({ }); + ok(typeof cObj.isControlVisible == 'function', 'isControlVisible should exist'); + + equal(cObj.isControlVisible('tl'), true); + equal(cObj.isControlVisible('tr'), true); + equal(cObj.isControlVisible('br'), true); + equal(cObj.isControlVisible('bl'), true); + equal(cObj.isControlVisible('ml'), true); + equal(cObj.isControlVisible('mt'), true); + equal(cObj.isControlVisible('mr'), true); + equal(cObj.isControlVisible('mb'), true); + equal(cObj.isControlVisible('mtr'), true); + }); + + test('setControlVisible', function(){ + ok(fabric.Object); + + var cObj = new fabric.Object({ }); + ok(typeof cObj.setControlVisible == 'function', 'setControlVisible should exist'); + equal(cObj.setControlVisible('tl'), cObj, 'chainable'); + + cObj.setControlVisible('tl', false); + equal(cObj.isControlVisible('tl'), false); + cObj.setControlVisible('tl', true); + equal(cObj.isControlVisible('tl'), true); + }); + + test('setControlsVisibility', function(){ + ok(fabric.Object); + + var cObj = new fabric.Object({ }); + ok(typeof cObj.setControlsVisibility == 'function', 'setControlsVisibility should exist'); + equal(cObj.setControlsVisibility(), cObj, 'chainable'); + + cObj.setControlsVisibility({ + bl: false, + br: false, + mb: false, + ml: false, + mr: false, + mt: false, + tl: false, + tr: false, + mtr: false + }); + + equal(cObj.isControlVisible('tl'), false); + equal(cObj.isControlVisible('tr'), false); + equal(cObj.isControlVisible('br'), false); + equal(cObj.isControlVisible('bl'), false); + equal(cObj.isControlVisible('ml'), false); + equal(cObj.isControlVisible('mt'), false); + equal(cObj.isControlVisible('mr'), false); + equal(cObj.isControlVisible('mb'), false); + equal(cObj.isControlVisible('mtr'), false); + + cObj.setControlsVisibility({ + bl: true, + br: true, + mb: true, + ml: true, + mr: true, + mt: true, + tl: true, + tr: true, + mtr: true + }); + + equal(cObj.isControlVisible('tl'), true); + equal(cObj.isControlVisible('tr'), true); + equal(cObj.isControlVisible('br'), true); + equal(cObj.isControlVisible('bl'), true); + equal(cObj.isControlVisible('ml'), true); + equal(cObj.isControlVisible('mt'), true); + equal(cObj.isControlVisible('mr'), true); + equal(cObj.isControlVisible('mb'), true); + equal(cObj.isControlVisible('mtr'), true); + }); + +})();