[zelos] Cross browser pixel perfect text input (#3466)

* Align text and widget div text input pixel perfect on all browsers
This commit is contained in:
Sam El-Husseini
2019-11-25 15:54:24 -08:00
committed by GitHub
parent 5ddf374327
commit 9fcc532977
6 changed files with 114 additions and 31 deletions

View File

@@ -393,6 +393,7 @@ Blockly.Css.CONTENT = [
'width: 100%;',
'text-align: center;',
'display: block;',
'box-sizing: border-box;',
'}',
/* Edge and IE introduce a close icon when the input value is longer than a

View File

@@ -344,22 +344,23 @@ Blockly.Field.prototype.createBorderRect_ = function() {
Blockly.Field.prototype.createTextElement_ = function() {
var xOffset = this.borderRect_ ?
this.constants_.FIELD_BORDER_RECT_X_PADDING : 0;
this.size_.height = Math.max(this.size_.height,
this.constants_.FIELD_TEXT_BASELINE_CENTER ?
this.constants_.FIELD_TEXT_HEIGHT :
this.constants_.FIELD_TEXT_BASELINE_Y);
var baselineCenter = this.constants_.FIELD_TEXT_BASELINE_CENTER;
var baselineY = this.constants_.FIELD_TEXT_BASELINE_Y;
this.size_.height = Math.max(this.size_.height, baselineCenter ?
this.constants_.FIELD_TEXT_HEIGHT : baselineY);
if (this.size_.height > this.constants_.FIELD_TEXT_HEIGHT) {
baselineY += (this.size_.height - baselineY) / 2;
}
this.textElement_ = /** @type {!SVGTextElement} **/
(Blockly.utils.dom.createSvgElement('text',
{
'class': 'blocklyText',
'y': this.size_.height / 2,
'y': baselineCenter ? this.size_.height / 2 : baselineY,
'dy': this.constants_.FIELD_TEXT_Y_OFFSET,
'x': xOffset
}, this.fieldGroup_));
if (this.constants_.FIELD_TEXT_BASELINE_CENTER) {
if (baselineCenter) {
this.textElement_.setAttribute('dominant-baseline', 'central');
} else {
this.textElement_.setAttribute('dy',
this.constants_.FIELD_TEXT_BASELINE_Y - this.size_.height / 2);
}
this.textContent_ = document.createTextNode('');
this.textElement_.appendChild(this.textContent_);
@@ -421,7 +422,7 @@ Blockly.Field.prototype.dispose = function() {
* Add or remove the UI indicating if this field is editable or not.
*/
Blockly.Field.prototype.updateEditable = function() {
var group = this.getClickTarget_();
var group = this.fieldGroup_;
if (!this.EDITABLE || !group) {
return;
}
@@ -673,9 +674,9 @@ Blockly.Field.prototype.getSize = function() {
*/
Blockly.Field.prototype.getScaledBBox = function() {
var bBox = this.borderRect_.getBBox();
var scaledHeight = bBox.height * this.sourceBlock_.workspace.scale;
var scaledWidth = bBox.width * this.sourceBlock_.workspace.scale;
var xy = this.getAbsoluteXY_();
var scaledWidth = bBox.width * this.sourceBlock_.workspace.scale;
var scaledHeight = bBox.height * this.sourceBlock_.workspace.scale;
return {
top: xy.y,
bottom: xy.y + scaledHeight,

View File

@@ -146,10 +146,6 @@ Blockly.FieldTextInput.prototype.configure_ = function(config) {
* @override
*/
Blockly.FieldTextInput.prototype.initView = function() {
this.size_.height = Math.max(this.constants_.FIELD_BORDER_RECT_HEIGHT,
this.constants_.FIELD_TEXT_BASELINE_CENTER ?
this.constants_.FIELD_TEXT_HEIGHT :
this.constants_.FIELD_TEXT_BASELINE_Y);
if (this.constants_.FULL_BLOCK_FIELDS) {
// Step one: figure out if this is the only field on this block.
// Rendering is quite different in that case.
@@ -181,9 +177,6 @@ Blockly.FieldTextInput.prototype.initView = function() {
this.createBorderRect_();
}
this.createTextElement_();
if (this.constants_.FIELD_TEXT_BASELINE_CENTER) {
this.textElement_.setAttribute('dominant-baseline', 'central');
}
};
/**
@@ -354,23 +347,33 @@ Blockly.FieldTextInput.prototype.widgetCreate_ = function() {
var htmlInput = /** @type {HTMLInputElement} */ (document.createElement('input'));
htmlInput.className = 'blocklyHtmlInput';
htmlInput.setAttribute('spellcheck', this.spellcheck_);
var scale = this.workspace_.scale;
var fontSize =
(this.constants_.FIELD_TEXT_FONTSIZE * this.workspace_.scale) + 'pt';
(this.constants_.FIELD_TEXT_FONTSIZE * scale) + 'pt';
div.style.fontSize = fontSize;
htmlInput.style.fontSize = fontSize;
var borderRadius =
(Blockly.FieldTextInput.BORDERRADIUS * this.workspace_.scale) + 'px';
(Blockly.FieldTextInput.BORDERRADIUS * scale) + 'px';
if (this.fullBlockClickTarget_) {
var bBox = this.getScaledBBox();
// Override border radius.
borderRadius = (bBox.bottom - bBox.top) / 2 + 'px';
// Pull stroke colour from the existing shadow block
var strokeColour = this.sourceBlock_.style.colourTertiary;
div.style.borderColor = strokeColour;
var strokeColour = this.sourceBlock_.getParent() ?
this.sourceBlock_.getParent().style.colourTertiary :
this.sourceBlock_.style.colourTertiary;
htmlInput.style.border = (1 * scale) + 'px solid ' + strokeColour;
div.style.borderRadius = borderRadius;
div.style.transition = 'box-shadow 0.25s ease 0s';
if (this.constants_.FIELD_TEXTINPUT_BOX_SHADOW) {
div.style.boxShadow = 'rgba(255, 255, 255, 0.3) 0px 0px 0px ' +
4 * scale + 'px';
}
}
htmlInput.style.borderRadius = borderRadius;
div.appendChild(htmlInput);
htmlInput.value = htmlInput.defaultValue = this.getEditorText_(this.value_);
@@ -406,6 +409,8 @@ Blockly.FieldTextInput.prototype.widgetDispose_ = function() {
style.width = 'auto';
style.height = 'auto';
style.fontSize = '';
style.transition = '';
style.boxShadow = '';
this.htmlInput_ = null;
Blockly.utils.dom.removeClass(this.getClickTarget_(), 'editing');
@@ -634,16 +639,39 @@ Blockly.FieldTextInput.prototype.getValueFromEditorText_ = function(text) {
* @override
*/
Blockly.FieldTextInput.prototype.getScaledBBox = function() {
if (this.fullBlockClickTarget_) {
var xy = this.getClickTarget_().getBoundingClientRect();
if (!this.borderRect_) {
// Browsers are inconsistent in what they return for a bounding box.
// - Webkit / Blink: fill-box / object bounding box
// - Gecko / Triden / EdgeHTML: stroke-box
var bBox = this.sourceBlock_.getHeightWidth();
var scale = this.sourceBlock_.workspace.scale;
var xy = this.getAbsoluteXY_();
var scaledWidth = bBox.width * scale;
var scaledHeight = bBox.height * scale;
if (Blockly.utils.userAgent.GECKO) {
xy.x += 1.5 * scale;
xy.y += 1.5 * scale;
scaledWidth += 1 * scale;
scaledHeight += 1 * scale;
} else {
if (!Blockly.utils.userAgent.EDGE && !Blockly.utils.userAgent.IE) {
xy.x -= 0.5 * scale;
xy.y -= 0.5 * scale;
}
scaledWidth += 1 * scale;
scaledHeight += 1 * scale;
}
} else {
var xy = this.borderRect_.getBoundingClientRect();
var scaledWidth = xy.width;
var scaledHeight = xy.height;
}
return {
top: xy.y,
bottom: xy.y + xy.height,
bottom: xy.y + scaledHeight,
left: xy.x,
right: xy.x + xy.width
right: xy.x + scaledWidth
};
};

View File

@@ -168,7 +168,7 @@ Blockly.blockRendering.ConstantProvider = function() {
* Height of text.
* @type {number}
*/
this.FIELD_TEXT_HEIGHT = 13;
this.FIELD_TEXT_HEIGHT = 16;
/**
* Text font weight. Should match blocklyText's font-weight in CSS.
@@ -207,12 +207,18 @@ Blockly.blockRendering.ConstantProvider = function() {
this.FIELD_BORDER_RECT_Y_PADDING = 3;
/**
* Field text baseline. This is only used if `FIELD_TEXT_BASELINE_CENTER` is
* set to false.
* Field text baseline.
* This is only used if `FIELD_TEXT_BASELINE_CENTER` is false.
* @type {number}
*/
this.FIELD_TEXT_BASELINE_Y = Blockly.utils.userAgent.GECKO ? 12 : 13.09;
/**
* An text offset adjusting the Y position of text after positioning.
* @type {number}
*/
this.FIELD_TEXT_Y_OFFSET = 0;
/**
* A field's text element's dominant baseline.
* @type {boolean}
@@ -232,6 +238,13 @@ Blockly.blockRendering.ConstantProvider = function() {
*/
this.FIELD_DROPDOWN_SVG_ARROW = false;
/**
* Whether or not to show a box shadow around the widget div. This is only a
* feature of full block fields.
* @type {boolean}
*/
this.FIELD_TEXTINPUT_BOX_SHADOW = false;
/**
* Whether or not the colour field should display its colour value on the
* entire block.

View File

@@ -169,6 +169,24 @@ Blockly.zelos.ConstantProvider = function() {
*/
this.FIELD_TEXT_FONTWEIGHT = 'bold';
/**
* @override
*/
this.FIELD_TEXT_FONTFAMILY =
'"Helvetica Neue", "Segoe UI", Helvetica, sans-serif';
/**
* @override
*/
this.FIELD_TEXT_HEIGHT = 13.1;
/**
* Used by positioning text on IE and Edge as they don't support
* dominant-baseline:center.
* @override
*/
this.FIELD_TEXT_BASELINE_Y = 13.1;
/**
* @override
*/
@@ -178,6 +196,16 @@ Blockly.zelos.ConstantProvider = function() {
* @override
*/
this.FIELD_BORDER_RECT_X_PADDING = 2 * this.GRID_UNIT;
/**
* @override
*/
this.FIELD_BORDER_RECT_Y_PADDING = 1 * this.GRID_UNIT;
/**
* @override
*/
this.FIELD_BORDER_RECT_HEIGHT = 8 * this.GRID_UNIT;
/**
* @override
@@ -214,6 +242,16 @@ Blockly.zelos.ConstantProvider = function() {
'AuNTYtLjU2LDkuMzEtMC41Niw5Ljg3LDBhMS40NCwxLjQ0LDAsMCwxLDAsMkw3LjM3LDcuMz' +
'dBMS40MywxLjQzLDAsMCwxLDYuMzYsNy43OVoiIGZpbGw9IiNmZmYiLz48L3N2Zz4=';
/**
* @override
*/
this.FIELD_TEXTINPUT_BOX_SHADOW = true;
/**
* @override
*/
this.FIELD_TEXT_Y_OFFSET = Blockly.utils.userAgent.CHROME ? -.45 : 0;
/**
* @override
*/

View File

@@ -51,6 +51,8 @@ goog.require('Blockly.utils.global');
// Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.44
// (KHTML, like Gecko) JavaFX/8.0 Safari/537.44
Blockly.utils.userAgent.JAVA_FX = has('JavaFX');
Blockly.utils.userAgent.CHROME = (has('Chrome') || has('CriOS')) &&
!Blockly.utils.userAgent.EDGE;
// Engines. Logic from:
// https://github.com/google/closure-library/blob/master/closure/goog/labs/useragent/engine.js