-
Notifications
You must be signed in to change notification settings - Fork 3.8k
Added JSON Customization Support For Angle Fields #2654
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -61,7 +61,47 @@ goog.inherits(Blockly.FieldAngle, Blockly.FieldTextInput); | |
| * @nocollapse | ||
| */ | ||
| Blockly.FieldAngle.fromJson = function(options) { | ||
| return new Blockly.FieldAngle(options['angle']); | ||
| var field = new Blockly.FieldAngle(options['angle']); | ||
|
|
||
| var clockwise = null; | ||
| var offset = null; | ||
| var wrap = null; | ||
| var round = null; | ||
|
|
||
| var mode = options['mode']; | ||
| switch (mode) { | ||
| case 'compass': | ||
| clockwise = true; | ||
| offset = 90; | ||
| break; | ||
| case 'protractor': | ||
| // This is the default mode, so we could do nothing. But just to | ||
| // future-proof we'll set it anyway. | ||
| clockwise = false; | ||
| offset = 0; | ||
| break; | ||
| } | ||
|
|
||
| // Allow individual settings to override the mode setting. | ||
| if (typeof options['clockwise'] == 'boolean') { | ||
| clockwise = options['clockwise']; | ||
| } | ||
| if (typeof options['offset'] == 'number') { | ||
| offset = options['offset']; | ||
| } | ||
| if (typeof options['wrap'] == 'number') { | ||
| wrap = options['wrap']; | ||
| } | ||
| if (typeof options['round'] == 'number') { | ||
| round = options['round']; | ||
| } | ||
|
|
||
| field.setClockwise((clockwise)); | ||
| field.setOffset(offset); | ||
| field.setWrap(wrap); | ||
| field.setRound(round); | ||
|
|
||
| return field; | ||
| }; | ||
|
|
||
| /** | ||
|
|
@@ -72,51 +112,83 @@ Blockly.FieldAngle.fromJson = function(options) { | |
| */ | ||
| Blockly.FieldAngle.prototype.SERIALIZABLE = true; | ||
|
|
||
| /** | ||
| * Round angles to the nearest 15 degrees when using mouse. | ||
| * Set to 0 to disable rounding. | ||
| */ | ||
| Blockly.FieldAngle.ROUND = 15; | ||
|
|
||
| /** | ||
| * Half the width of protractor image. | ||
| * @type {number} | ||
| * @const | ||
| */ | ||
| Blockly.FieldAngle.HALF = 100 / 2; | ||
|
|
||
| /* The following two settings work together to set the behaviour of the angle | ||
| * picker. While many combinations are possible, two modes are typical: | ||
| * Math mode. | ||
| * 0 deg is right, 90 is up. This is the style used by protractors. | ||
| * Blockly.FieldAngle.CLOCKWISE = false; | ||
| * Blockly.FieldAngle.OFFSET = 0; | ||
| * Compass mode. | ||
| * 0 deg is up, 90 is right. This is the style used by maps. | ||
| * Blockly.FieldAngle.CLOCKWISE = true; | ||
| * Blockly.FieldAngle.OFFSET = 90; | ||
| /** | ||
| * Radius of protractor circle. Slightly smaller than protractor size since | ||
| * otherwise SVG crops off half the border at the edges. | ||
| * @type {number} | ||
| * @const | ||
| */ | ||
| Blockly.FieldAngle.RADIUS = Blockly.FieldAngle.HALF - 1; | ||
|
|
||
| /* See <DOCS LINK HERE> for more information about these properties. */ | ||
|
|
||
| /** | ||
| * Angle increases clockwise (true) or counterclockwise (false). | ||
| * @type {boolean} | ||
| * @const | ||
| */ | ||
| Blockly.FieldAngle.CLOCKWISE = false; | ||
|
|
||
| /** | ||
| * Offset the location of 0 degrees (and all angles) by a constant. | ||
| * Offset the location of 0 degrees (and all angles) by a constant number of | ||
| * degrees in the clockwise direction (independent of the CLOCKWISE property). | ||
| * Usually either 0 (0 = right) or 90 (0 = up). | ||
| * @type {number} | ||
| * @const | ||
| */ | ||
| Blockly.FieldAngle.OFFSET = 0; | ||
|
|
||
| /** | ||
| * Maximum allowed angle before wrapping. | ||
| * The wrap/range of the angle field. The range is equal to (-360 + WRAP, WRAP). | ||
| * Usually either 360 (for 0 to 359.9) or 180 (for -179.9 to 180). | ||
| * @type {number} | ||
| * @const | ||
| */ | ||
| Blockly.FieldAngle.WRAP = 360; | ||
|
|
||
| /** | ||
| * Radius of protractor circle. Slightly smaller than protractor size since | ||
| * otherwise SVG crops off half the border at the edges. | ||
| * Round angles input through the graphical editor (mouse) to the nearest | ||
| * multiple of this number. | ||
| * Set to 0 to disable rounding. | ||
| * @type {number} | ||
| * @const | ||
| */ | ||
| Blockly.FieldAngle.RADIUS = Blockly.FieldAngle.HALF - 1; | ||
| Blockly.FieldAngle.ROUND = 15; | ||
|
|
||
| /** | ||
| * The clockwise property used by this field. If null, use the global property. | ||
| * @type {?boolean} | ||
| * @private | ||
| */ | ||
| Blockly.FieldAngle.prototype.clockwise_ = null; | ||
|
|
||
| /** | ||
| * The offset property used by this field. If null, use the global property. | ||
| * @type {?number} | ||
| * @private | ||
| */ | ||
| Blockly.FieldAngle.prototype.offset_ = null; | ||
|
|
||
| /** | ||
| * The wrap property used by this field. If null, use the global property. | ||
| * @type {?number} | ||
| * @private | ||
| */ | ||
| Blockly.FieldAngle.prototype.wrap_ = null; | ||
|
|
||
| /** | ||
| * The round property used by this field. If null, use the global property. | ||
| * @type {?number} | ||
| * @private | ||
| */ | ||
| Blockly.FieldAngle.prototype.round_ = null; | ||
|
|
||
| /** | ||
| * Create the block UI for this field. | ||
|
|
@@ -262,23 +334,24 @@ Blockly.FieldAngle.prototype.onMouseMove = function(e) { | |
| } | ||
|
|
||
| // Do offsetting. | ||
| if (Blockly.FieldAngle.CLOCKWISE) { | ||
| angle = Blockly.FieldAngle.OFFSET + 360 - angle; | ||
| var offset = this.getOffset(); | ||
| if (this.getClockwise()) { | ||
| angle = offset + 360 - angle; | ||
| } else { | ||
| angle = 360 - (Blockly.FieldAngle.OFFSET - angle); | ||
| angle = 360 - (offset - angle); | ||
| } | ||
| if (angle > 360) { | ||
| angle -= 360; | ||
| } | ||
|
|
||
| // Do rounding. | ||
| if (Blockly.FieldAngle.ROUND) { | ||
| angle = Math.round(angle / Blockly.FieldAngle.ROUND) * | ||
| Blockly.FieldAngle.ROUND; | ||
| var round = this.getRound(); | ||
| if (round) { | ||
| angle = Math.round(angle / round) * round; | ||
| } | ||
|
|
||
| // Do wrapping. | ||
| if (angle > Blockly.FieldAngle.WRAP) { | ||
| if (angle > this.getWrap()) { | ||
| angle -= 360; | ||
| } | ||
|
|
||
|
|
@@ -301,28 +374,31 @@ Blockly.FieldAngle.prototype.updateGraph_ = function() { | |
| if (!this.gauge_) { | ||
| return; | ||
| } | ||
| var clockwise = this.getClockwise(); | ||
| var offset = this.getOffset(); | ||
|
|
||
| // Always display the input (i.e. getText) even if it is invalid. | ||
| var angleDegrees = Number(this.getText()) + Blockly.FieldAngle.OFFSET; | ||
| var angleDegrees = Number(this.getText()) + offset; | ||
| angleDegrees %= 360; | ||
| var angleRadians = Blockly.utils.math.toRadians(angleDegrees); | ||
| var path = ['M ', Blockly.FieldAngle.HALF, ',', Blockly.FieldAngle.HALF]; | ||
| var x2 = Blockly.FieldAngle.HALF; | ||
| var y2 = Blockly.FieldAngle.HALF; | ||
| if (!isNaN(angleRadians)) { | ||
| var angle1 = Blockly.utils.math.toRadians(Blockly.FieldAngle.OFFSET); | ||
| var angle1 = Blockly.utils.math.toRadians(offset); | ||
| var x1 = Math.cos(angle1) * Blockly.FieldAngle.RADIUS; | ||
| var y1 = Math.sin(angle1) * -Blockly.FieldAngle.RADIUS; | ||
| if (Blockly.FieldAngle.CLOCKWISE) { | ||
| if (clockwise) { | ||
| angleRadians = 2 * angle1 - angleRadians; | ||
| } | ||
| x2 += Math.cos(angleRadians) * Blockly.FieldAngle.RADIUS; | ||
| y2 -= Math.sin(angleRadians) * Blockly.FieldAngle.RADIUS; | ||
| // Don't ask how the flag calculations work. They just do. | ||
| var largeFlag = Math.abs(Math.floor((angleRadians - angle1) / Math.PI) % 2); | ||
| if (Blockly.FieldAngle.CLOCKWISE) { | ||
| if (clockwise) { | ||
| largeFlag = 1 - largeFlag; | ||
| } | ||
| var sweepFlag = Number(Blockly.FieldAngle.CLOCKWISE); | ||
| var sweepFlag = Number(clockwise); | ||
| path.push(' l ', x1, ',', y1, | ||
| ' A ', Blockly.FieldAngle.RADIUS, ',', Blockly.FieldAngle.RADIUS, | ||
| ' 0 ', largeFlag, ' ', sweepFlag, ' ', x2, ',', y2, ' z'); | ||
|
|
@@ -348,10 +424,104 @@ Blockly.FieldAngle.prototype.doClassValidation_ = function(newValue) { | |
| if (n < 0) { | ||
| n += 360; | ||
| } | ||
| if (n > Blockly.FieldAngle.WRAP) { | ||
| if (n > this.getWrap()) { | ||
| n -= 360; | ||
| } | ||
| return n; | ||
| }; | ||
|
|
||
| /** | ||
| * Set which direction should make the graphical angle editor increase. | ||
| * @param {?boolean} clockwise Whether the graphical angle editor should | ||
| * increase as it is moved clockwise (true) or counterclockwise (false) | ||
| * or the global setting (null). | ||
| * @return {!Blockly.FieldAngle} Returns itself (for method chaining). | ||
| */ | ||
| Blockly.FieldAngle.prototype.setClockwise = function(clockwise) { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @RoboErikG thoughts on returning itself for method chaining? It's a reasonable pattern, but 1) we don't use it anywhere else and 2) we probably want to be consistent.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's nice for convenience, but it's pretty non-standard for instances. I think I'd rather start using an opt_config parameter on the constructor with a map of the configuration options. The keys should be the same as the JSON config which will also let you use the same code for both.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you want me to go ahead and update it? or are you waiting for feedback from Rachel?
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. +1 to Erik. Go ahead and update it.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I went to go update this, and then I realized I had more questions and wasn't sure how far reaching the change should be. I created an issue to make discussion easier.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you're happy with the clarification on #2722, then just remove the method chaining and this will be good to go.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was planning on getting the abstract field changes in first (just to make sure how I'm thinking about it in my head is actually going to work). But don't worry I haven't forgotten about this little guy! Also atm #2722 has this guy down as no hooks. This is b/c, for example, changing the WRAP value at runtime means the field would have to do some extra work recalculating it's value. I think I could get it to work though. Did you want hooks for the angle field or no? |
||
| this.clockwise_ = clockwise; | ||
| return this; | ||
| }; | ||
|
|
||
| /** | ||
| * Set the offset of zero degrees. Usually 0 (right) or 90 (up). | ||
| * @param {?number} offset The amount to offset the location of 0 degrees | ||
| * by. Always offsets in the clockwise direction independent of the | ||
| * Clockwise property. Pass null to use the | ||
| * global setting. | ||
| * @return {!Blockly.FieldAngle} Returns itself (for method chaining). | ||
| */ | ||
| Blockly.FieldAngle.prototype.setOffset = function(offset) { | ||
| this.offset_ = offset; | ||
| return this; | ||
| }; | ||
|
|
||
| /** | ||
| * Set the wrap/range of the angle field. The range is equal to (-360 + | ||
| * WRAP, WRAP). | ||
| * Usually either 360 (for 0 to 359.9) or 180 (for -179.9 to 180). | ||
| * @param {?number} wrap The wrap value of the angle field. Pass null to use | ||
| * the global setting. | ||
| * @return {!Blockly.FieldAngle} Returns itself (for method chaining). | ||
| */ | ||
| Blockly.FieldAngle.prototype.setWrap = function(wrap) { | ||
| this.wrap_ = wrap; | ||
| return this; | ||
| }; | ||
|
|
||
| /** | ||
| * Set the rounding of the angle field's graphical editor. | ||
| * @param {?number} round The value (when input through the graphical | ||
| * editor) will be rounded to the nearest multiple of this number. Pass 0 | ||
| * to disable rounding. Pass null to use the global setting. | ||
| * @return {!Blockly.FieldAngle} Returns itself (for method chaining). | ||
| */ | ||
| Blockly.FieldAngle.prototype.setRound = function(round) { | ||
| this.round_ = round; | ||
| return this; | ||
| }; | ||
|
|
||
| /** | ||
| * Get the direction the angle field's value increases in. | ||
| * @return {boolean} Clockwise (true) or counterclockwise (false). | ||
| */ | ||
| Blockly.FieldAngle.prototype.getClockwise = function() { | ||
| if (this.clockwise_ == null) { | ||
| return Blockly.FieldAngle.CLOCKWISE; | ||
| } | ||
| return this.clockwise_; | ||
| }; | ||
|
|
||
| /** | ||
| * Get the amount 0 degrees is offset by. | ||
| * @return {number} The amount of offset. | ||
| */ | ||
| Blockly.FieldAngle.prototype.getOffset = function() { | ||
| if (this.offset_ == null) { | ||
| return Blockly.FieldAngle.OFFSET; | ||
| } | ||
| return this.offset_; | ||
| }; | ||
|
|
||
| /** | ||
| * Get the wrap value of the angle field. | ||
| * @return {number} The number the angle field gets wrapped at. | ||
| */ | ||
| Blockly.FieldAngle.prototype.getWrap = function() { | ||
| if (this.wrap_ == null) { | ||
| return Blockly.FieldAngle.WRAP; | ||
| } | ||
| return this.wrap_; | ||
| }; | ||
|
|
||
| /** | ||
| * Get the number values input via the graphical editor are rounded to. | ||
| * @return {number} The round value. | ||
| */ | ||
| Blockly.FieldAngle.prototype.getRound = function() { | ||
| if (this.round_ == null) { | ||
| return Blockly.FieldAngle.ROUND; | ||
| } | ||
| return this.round_; | ||
| }; | ||
|
|
||
| Blockly.Field.register('field_angle', Blockly.FieldAngle); | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Needs a link.