diff --git a/pxteditor/editor.ts b/pxteditor/editor.ts index 11c5b373d426..4f763766ab5a 100644 --- a/pxteditor/editor.ts +++ b/pxteditor/editor.ts @@ -14,7 +14,9 @@ namespace pxt.editor { zoomIn(): void; zoomOut(): void; resize(): void; + getMaxScale(): number; setScale(scale: number): void; + onScaleChanged: (oldScale: number, newScale: number) => void; } export interface IFile { @@ -102,6 +104,7 @@ namespace pxt.editor { extensionsVisible?: boolean; isMultiplayerGame?: boolean; // Arcade: Does the current project contain multiplayer blocks? onboarding?: pxt.tour.BubbleStep[]; + tutorialFontSize?: number; } export interface EditorState { diff --git a/theme/tutorial-sidebar.less b/theme/tutorial-sidebar.less index 13dcf0437963..3d87d1da89c9 100644 --- a/theme/tutorial-sidebar.less +++ b/theme/tutorial-sidebar.less @@ -1,5 +1,4 @@ @defaultTutorialHeight: 18.625rem; -@tutorialFontSize: 1.125rem; @tutorialTitleFontSize: 1.5rem; @tutorialPrimaryColor: @sidebarPrimaryColor; @@ -55,7 +54,7 @@ overflow-x: hidden; overflow-y: auto; font-family: @segoeUIFont; - font-size: @tutorialFontSize; + font-size: var(--tutorialFontSize); span.docs.inlineblock { white-space: break-spaces; @@ -248,7 +247,7 @@ margin: 0 1rem; color: @white; background: @tutorialPrimaryColor; - font-size: @tutorialFontSize; + font-size: var(--tutorialFontSize); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; diff --git a/webapp/src/app.tsx b/webapp/src/app.tsx index 8137525ce49d..9bcca07192d7 100644 --- a/webapp/src/app.tsx +++ b/webapp/src/app.tsx @@ -152,6 +152,10 @@ export class ProjectView private rootClasses: string[]; private pendingImport: pxt.Util.DeferredPromise; + private initialEditorScale: number; + private tutorialInitialFontSize = 1.125; // rem + private tutorialMaxFontSize = 2; // rem + private highContrastSubscriber: data.DataSubscriber = { subscriptions: [], onDataChanged: () => { @@ -211,6 +215,7 @@ export class ProjectView this.exitTutorial = this.exitTutorial.bind(this); this.setEditorOffset = this.setEditorOffset.bind(this); this.resetTutorialTemplateCode = this.resetTutorialTemplateCode.bind(this); + this.onScaleChanged = this.onScaleChanged.bind(this); this.initSimulatorMessageHandlers(); // add user hint IDs and callback to hint manager @@ -1029,7 +1034,10 @@ export class ProjectView } this.allEditors = [this.pxtJsonEditor, this.gitjsonEditor, this.blocksEditor, this.serialEditor, this.assetEditor, this.textEditor] this.allEditors.forEach(e => e.changeCallback = changeHandler) + this.allEditors.forEach(e => e.onScaleChanged = this.onScaleChanged) + this.editor = this.allEditors[this.allEditors.length - 1] + this.initialEditorScale = undefined; } public UNSAFE_componentWillMount() { @@ -1108,6 +1116,8 @@ export class ProjectView // subscribe to user preference changes (for simulator or non-render subscriptions) data.subscribe(this.highContrastSubscriber, auth.HIGHCONTRAST); data.subscribe(this.cloudStatusSubscriber, `${cloud.HEADER_CLOUDSTATE}:*`); + + document.getElementById("root").style.setProperty("--tutorialFontSize", `${this.tutorialInitialFontSize}rem`); } public componentWillUnmount() { @@ -1532,6 +1542,26 @@ export class ProjectView } } + onScaleChanged(oldScale: number, newScale: number) { + if (this.isTutorial && oldScale !== newScale) { + if (this.initialEditorScale === undefined) { + this.initialEditorScale = oldScale; + } + + const root = document.getElementById("root"); + if (root && newScale <= this.initialEditorScale) { + // Do not shrink the text beyond its initial size. + root.style.setProperty("--tutorialFontSize", `${this.tutorialInitialFontSize}rem`); + } else if (root) { + // Increase font size to match the editor's % zoom. + const maxEditorScale = this.editor.getMaxScale(); + const zoomAmt = (newScale - this.initialEditorScale) / (maxEditorScale - this.initialEditorScale); + const newTutorialFontSize = this.tutorialInitialFontSize + (this.tutorialMaxFontSize - this.tutorialInitialFontSize) * zoomAmt; + root.style.setProperty("--tutorialFontSize", `${newTutorialFontSize}rem`); + } + } + } + /////////////////////////////////////////////////////////// //////////// Load header ///////////// /////////////////////////////////////////////////////////// diff --git a/webapp/src/blocks.tsx b/webapp/src/blocks.tsx index 7014087ddc72..d3972072427b 100644 --- a/webapp/src/blocks.tsx +++ b/webapp/src/blocks.tsx @@ -31,6 +31,7 @@ export class Editor extends toolboxeditor.ToolboxEditor { compilationResult: pxt.blocks.BlockCompilationResult; isFirstBlocklyLoad = true; functionsDialog: CreateFunctionDialog = null; + onScaleChanged: (oldScale: number, newScale: number) => void; showCategories: boolean = true; breakpointsByBlock: pxt.Map; // Map block id --> breakpoint ID @@ -48,6 +49,10 @@ export class Editor extends toolboxeditor.ToolboxEditor { public nsMap: pxt.Map; + // Blockly fires a scale event when it loads, but the user hasn't actually changed the scale. + // We use this flag to ignore that initial event. + private initialScaleSet: boolean = false; + constructor(parent: pxt.editor.IProjectView) { super(parent); @@ -581,7 +586,6 @@ export class Editor extends toolboxeditor.ToolboxEditor { Blockly.Events.UI, Blockly.Events.SELECTED, Blockly.Events.CLICK, - Blockly.Events.VIEWPORT_CHANGE, Blockly.Events.BUBBLE_OPEN ]; @@ -636,6 +640,13 @@ export class Editor extends toolboxeditor.ToolboxEditor { } } } + else if (ev.type === Blockly.Events.VIEWPORT_CHANGE) { + const viewportChangeEvent = ev as Blockly.Events.ViewportChange; + if (this.initialScaleSet && viewportChangeEvent.oldScale !== viewportChangeEvent.scale && this.onScaleChanged) { + this.onScaleChanged(viewportChangeEvent.oldScale, viewportChangeEvent.scale); + } + this.initialScaleSet = true; + } // reset tutorial hint animation on any blockly event if (this.parent.state.tutorialOptions != undefined) { @@ -753,6 +764,11 @@ export class Editor extends toolboxeditor.ToolboxEditor { this.editor.zoomCenter(-0.8); } + getMaxScale() { + if (!this.editor) return undefined; + return this.editor.options.zoomOptions.maxScale; + } + setScale(scale: number) { if (!this.editor) return; if (scale != this.editor.scale) { diff --git a/webapp/src/monaco.tsx b/webapp/src/monaco.tsx index 604f21ecfdc1..f1528f894685 100644 --- a/webapp/src/monaco.tsx +++ b/webapp/src/monaco.tsx @@ -340,6 +340,7 @@ export class Editor extends toolboxeditor.ToolboxEditor { extraLibs: pxt.Map; nsMap: pxt.Map; giveFocusOnLoading: boolean = true; + onScaleChanged: (oldScale: number, newScale: number) => void; protected fieldEditors: FieldEditorManager; protected feWidget: ViewZoneEditorHost | ModalEditorHost; @@ -968,9 +969,13 @@ export class Editor extends toolboxeditor.ToolboxEditor { this.editor.onDidLayoutChange((e: monaco.editor.EditorLayoutInfo) => { // Update editor font size in settings after a ctrl+scroll zoom let currentFont = this.getEditorFontSize(); - if (this.parent.settings.editorFontSize != currentFont) { + let prevFont = this.parent.settings.editorFontSize; + if (prevFont != currentFont) { this.parent.settings.editorFontSize = currentFont; this.forceDiagnosticsUpdate(); + if(this.onScaleChanged) { + this.onScaleChanged(prevFont, currentFont); + } } // Update widgets const toolbox = document.getElementById('monacoToolboxDiv'); @@ -1184,6 +1189,10 @@ export class Editor extends toolboxeditor.ToolboxEditor { this.parent.settings.editorFontSize = currentFont + 1; this.editor.updateOptions({ fontSize: this.parent.settings.editorFontSize }); this.forceDiagnosticsUpdate(); + + if (this.onScaleChanged) { + this.onScaleChanged(currentFont, this.parent.settings.editorFontSize); + } } zoomOut() { @@ -1193,6 +1202,15 @@ export class Editor extends toolboxeditor.ToolboxEditor { this.parent.settings.editorFontSize = currentFont - 1; this.editor.updateOptions({ fontSize: this.parent.settings.editorFontSize }); this.forceDiagnosticsUpdate(); + + if (this.onScaleChanged) { + this.onScaleChanged(currentFont, this.parent.settings.editorFontSize); + } + } + + getMaxScale() { + if (!this.editor) return undefined; + return MAX_EDITOR_FONT_SIZE; } private loadReference() { diff --git a/webapp/src/srceditor.tsx b/webapp/src/srceditor.tsx index 7227241066f3..4db7e9adb037 100644 --- a/webapp/src/srceditor.tsx +++ b/webapp/src/srceditor.tsx @@ -14,6 +14,7 @@ export class Editor implements pxt.editor.IEditor { isVisible = false; constructor(public parent: ProjectView) { } + onScaleChanged: (oldScale: number, newScale: number) => void; changeCallback = () => { }; setVisible(v: boolean) { this.isVisible = v; @@ -86,6 +87,7 @@ export class Editor implements pxt.editor.IEditor { zoomIn() { } zoomOut() { } + getMaxScale() { return 1; } setScale(scale: number) { } closeFlyout() { }