From 296fb0e7e1842c21c12e0b207f75dc0d41c171d3 Mon Sep 17 00:00:00 2001 From: Sarah Rietkerk Date: Fri, 6 Mar 2026 18:53:00 +0000 Subject: [PATCH 1/2] add the globe icon to variables --- .../plugins/newVariableField/fieldVariable.ts | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/pxtblocks/plugins/newVariableField/fieldVariable.ts b/pxtblocks/plugins/newVariableField/fieldVariable.ts index bdbae9f58c63..375adbbecc25 100644 --- a/pxtblocks/plugins/newVariableField/fieldVariable.ts +++ b/pxtblocks/plugins/newVariableField/fieldVariable.ts @@ -1,6 +1,12 @@ import * as Blockly from "blockly"; import { showEditorMixin } from "./fieldDropdownMixin"; +import svg = pxt.svgUtil; + +const ICON_WIDTH = 20; +const ICON_PADDING = 8; +const TEXT_ARROW_PADDING = 15; // Extra padding between text end and arrow + /** * This is the same as the Blockly variable field but with the addition * of a "New Variable" option in the dropdown @@ -58,6 +64,7 @@ export class FieldVariable extends Blockly.FieldVariable { private svgRootBinding: Blockly.browserEvents.Data | null = null; private fieldRootBinding: Blockly.browserEvents.Data | null = null; private clickTargetRect: SVGRectElement; + private globeIcon: svg.Text; override initView() { super.initView(); @@ -75,6 +82,13 @@ export class FieldVariable extends Blockly.FieldVariable { // Make sure to unset the border rect so that it isn't included in size // calculations this.borderRect_ = undefined; + + // Add globe icon + this.globeIcon = new svg.Text("\uf0ac") + .setClass("semanticIcon") + .setAttribute("alignment-baseline", "middle") + .anchor("middle"); + this.fieldGroup_.appendChild(this.globeIcon.el); } override shouldAddBorderRect_() { @@ -150,6 +164,17 @@ export class FieldVariable extends Blockly.FieldVariable { } } + protected override updateSize_(margin?: number): void { + // Let parent calculate the base size first + super.updateSize_(margin); + + // Then add extra width for the icon if we're rendering it + if (this.globeIcon && !this.shouldAddBorderRect_()) { + // Add space for: icon + padding between icon and text + extra padding after text for arrow + this.size_.width += ICON_WIDTH + ICON_PADDING + TEXT_ARROW_PADDING; + } + } + protected override positionBorderRect_() { super.positionBorderRect_(); @@ -167,6 +192,53 @@ export class FieldVariable extends Blockly.FieldVariable { 'ry', String(this.getConstants()!.FIELD_BORDER_RECT_RADIUS), ); + + // Position globe icon + if (this.globeIcon) { + this.globeIcon.at(ICON_WIDTH / 2, this.size_.height / 2); + } + } + + protected override render_() { + super.render_(); + + // After parent renders, shift all children (except the icon) to make room for icon + if (this.globeIcon && !this.shouldAddBorderRect_() && this.fieldGroup_) { + const children = this.fieldGroup_.children; + for (let i = 0; i < children.length; i++) { + const child = children[i] as SVGElement; + + // Skip the globe icon itself + if (child === this.globeIcon.el) { + continue; + } + + // Shift elements with x attribute (like text) + if (child.hasAttribute('x')) { + const currentX = parseFloat(child.getAttribute('x') || '0'); + child.setAttribute('x', String(currentX + ICON_WIDTH + ICON_PADDING)); + } + + // Shift elements with transform attribute (like arrow) + const transform = child.getAttribute('transform'); + if (transform) { + const match = transform.match(/translate\(([-\d.]+),\s*([-\d.]+)\)/); + if (match) { + const x = parseFloat(match[1]); + const y = parseFloat(match[2]); + child.setAttribute('transform', `translate(${x + ICON_WIDTH + ICON_PADDING}, ${y})`); + } + } + } + + // Update the width to account for the icon and shifted elements + this.size_.width += ICON_WIDTH + ICON_PADDING; + + // Update the click target rect to cover the new width + if (this.clickTargetRect) { + this.clickTargetRect.setAttribute('width', String(this.size_.width)); + } + } } protected showEditor_(e?: MouseEvent): void { From bca55391e23fba8f423c3c91a5087dc64692760e Mon Sep 17 00:00:00 2001 From: Sarah Rietkerk Date: Fri, 6 Mar 2026 19:07:39 +0000 Subject: [PATCH 2/2] only render the globe if the variable is global --- .../plugins/newVariableField/fieldVariable.ts | 25 ++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/pxtblocks/plugins/newVariableField/fieldVariable.ts b/pxtblocks/plugins/newVariableField/fieldVariable.ts index 375adbbecc25..0d38ce9121af 100644 --- a/pxtblocks/plugins/newVariableField/fieldVariable.ts +++ b/pxtblocks/plugins/newVariableField/fieldVariable.ts @@ -1,5 +1,6 @@ import * as Blockly from "blockly"; import { showEditorMixin } from "./fieldDropdownMixin"; +import { EXPORTED_VARIABLE_TYPE, IMPORTED_VARIABLE_TYPE } from "../../blocksProgram"; import svg = pxt.svgUtil; @@ -66,6 +67,16 @@ export class FieldVariable extends Blockly.FieldVariable { private clickTargetRect: SVGRectElement; private globeIcon: svg.Text; + /** + * Check if the current variable is a global variable (exported or imported) + */ + protected isGlobalVariable(): boolean { + const variable = this.getVariable(); + if (!variable) return false; + const varType = variable.getType(); + return varType === EXPORTED_VARIABLE_TYPE || varType === IMPORTED_VARIABLE_TYPE; + } + override initView() { super.initView(); @@ -83,12 +94,14 @@ export class FieldVariable extends Blockly.FieldVariable { // calculations this.borderRect_ = undefined; - // Add globe icon - this.globeIcon = new svg.Text("\uf0ac") - .setClass("semanticIcon") - .setAttribute("alignment-baseline", "middle") - .anchor("middle"); - this.fieldGroup_.appendChild(this.globeIcon.el); + // Add globe icon only for global variables + if (this.isGlobalVariable()) { + this.globeIcon = new svg.Text("\uf0ac") + .setClass("semanticIcon") + .setAttribute("alignment-baseline", "middle") + .anchor("middle"); + this.fieldGroup_.appendChild(this.globeIcon.el); + } } override shouldAddBorderRect_() {