diff --git a/src.compiler/csharp/CSharpAstTransformer.ts b/src.compiler/csharp/CSharpAstTransformer.ts index 524c154f2..c9f8fd2d4 100644 --- a/src.compiler/csharp/CSharpAstTransformer.ts +++ b/src.compiler/csharp/CSharpAstTransformer.ts @@ -551,7 +551,7 @@ export default class CSharpAstTransformer { name: this._context.toPascalCase((d.name as ts.Identifier).text), parameters: [], returnType: this.createUnresolvedTypeNode(null, d.type ?? d, returnType), - visibility: this.mapVisibility(d.modifiers), + visibility: this.mapVisibility(d), tsNode: d, skipEmit: this.shouldSkip(d, true) }; @@ -967,7 +967,7 @@ export default class CSharpAstTransformer { name: propertyName, nodeType: cs.SyntaxKind.PropertyDeclaration, parent: parent, - visibility: this.mapVisibility(classElement.modifiers), + visibility: this.mapVisibility(classElement), type: this.createUnresolvedTypeNode(null, classElement.type ?? classElement, returnType), skipEmit: this.shouldSkip(classElement, false) }; @@ -1038,7 +1038,7 @@ export default class CSharpAstTransformer { name: propertyName, nodeType: cs.SyntaxKind.PropertyDeclaration, parent: parent, - visibility: this.mapVisibility(classElement.modifiers), + visibility: this.mapVisibility(classElement), type: this.createUnresolvedTypeNode(null, classElement.type ?? classElement, returnType), skipEmit: this.shouldSkip(classElement, false) }; @@ -1091,7 +1091,7 @@ export default class CSharpAstTransformer { parent: cs.ClassDeclaration | cs.InterfaceDeclaration, classElement: ts.PropertyDeclaration ) { - const visibility = this.mapVisibility(classElement.modifiers); + const visibility = this.mapVisibility(classElement); const type = this._context.typeChecker.getTypeAtLocation(classElement); const csProperty: cs.PropertyDeclaration = { parent: parent, @@ -1203,7 +1203,7 @@ export default class CSharpAstTransformer { name: this._context.toPascalCase((classElement.name as ts.Identifier).text), parameters: [], returnType: this.createUnresolvedTypeNode(null, classElement.type ?? classElement, returnType), - visibility: this.mapVisibility(classElement.modifiers), + visibility: this.mapVisibility(classElement), tsNode: classElement, skipEmit: this.shouldSkip(classElement, false) }; @@ -1852,9 +1852,13 @@ export default class CSharpAstTransformer { this._context.registerSymbol(csMethod); } - protected mapVisibility(modifiers: ts.ModifiersArray | undefined): cs.Visibility { - if (modifiers) { - for (const m of modifiers) { + protected mapVisibility(node: ts.Node): cs.Visibility { + if(this._context.isInternal(node)) { + return cs.Visibility.Internal; + } + + if (node.modifiers) { + for (const m of node.modifiers) { switch (m.kind) { case ts.SyntaxKind.PublicKeyword: return cs.Visibility.Public; @@ -1913,7 +1917,7 @@ export default class CSharpAstTransformer { name: '.ctor', parameters: [], isStatic: false, - visibility: this.mapVisibility(classElement.modifiers), + visibility: this.mapVisibility(classElement), tsNode: classElement, skipEmit: this.shouldSkip(classElement, false) }; diff --git a/src.compiler/csharp/CSharpEmitterContext.ts b/src.compiler/csharp/CSharpEmitterContext.ts index fbdcd21dd..0a495f0e1 100644 --- a/src.compiler/csharp/CSharpEmitterContext.ts +++ b/src.compiler/csharp/CSharpEmitterContext.ts @@ -41,7 +41,7 @@ export default class CSharpEmitterContext { } public registerUnresolvedTypeNode(unresolved: cs.UnresolvedTypeNode) { - if(this.processingSkippedElement) { + if (this.processingSkippedElement) { return; } this._unresolvedTypeNodes.push(unresolved); @@ -479,7 +479,11 @@ export default class CSharpEmitterContext { return this.createBasicFunctionType(node, returnType, parameterTypes); } - protected createBasicFunctionType(node:cs.Node, returnType: cs.TypeNode, parameterTypes: cs.TypeNode[]): cs.TypeNode { + protected createBasicFunctionType( + node: cs.Node, + returnType: cs.TypeNode, + parameterTypes: cs.TypeNode[] + ): cs.TypeNode { return { nodeType: cs.SyntaxKind.FunctionTypeNode, parent: node.parent, @@ -488,7 +492,7 @@ export default class CSharpEmitterContext { returnType: returnType } as cs.FunctionTypeNode; } - + private resolveUnionType( parent: cs.Node, tsType: ts.Type, @@ -784,12 +788,13 @@ export default class CSharpEmitterContext { return text; } - if(!text) { + if (!text) { return ''; } - return text.split('.') - .map(p=> p.substr(0, 1).toUpperCase() + p.substr(1)) + return text + .split('.') + .map(p => p.substr(0, 1).toUpperCase() + p.substr(1)) .join('.'); } @@ -994,10 +999,12 @@ export default class CSharpEmitterContext { symbol = this.typeChecker.getAliasedSymbol(symbol); } - if (symbol.flags & ts.SymbolFlags.Interface || + if ( + symbol.flags & ts.SymbolFlags.Interface || symbol.flags & ts.SymbolFlags.Class || symbol.flags & ts.SymbolFlags.BlockScopedVariable || - symbol.flags & ts.SymbolFlags.FunctionScopedVariable) { + symbol.flags & ts.SymbolFlags.FunctionScopedVariable + ) { return false; } @@ -1176,11 +1183,7 @@ export default class CSharpEmitterContext { return this.hasAnyBaseTypeClassMember(classType, classElement.name!.getText()); } - protected hasAnyBaseTypeClassMember( - classType: ts.Type, - memberName: string, - allowInterfaces: boolean = false - ) { + protected hasAnyBaseTypeClassMember(classType: ts.Type, memberName: string, allowInterfaces: boolean = false) { const baseTypes = classType.getBaseTypes(); if (!baseTypes) { return false; @@ -1211,10 +1214,10 @@ export default class CSharpEmitterContext { } public isValueTypeExpression(expression: ts.NonNullExpression) { - let tsType:ts.Type; - if(ts.isIdentifier(expression.expression)) { + let tsType: ts.Type; + if (ts.isIdentifier(expression.expression)) { const symbol = this.typeChecker.getSymbolAtLocation(expression.expression); - if(symbol?.valueDeclaration) { + if (symbol?.valueDeclaration) { tsType = this.typeChecker.getTypeAtLocation(symbol.valueDeclaration); } else { tsType = this.typeChecker.getTypeAtLocation(expression); @@ -1243,6 +1246,10 @@ export default class CSharpEmitterContext { return false; } + public isInternal(node: ts.Node) { + return !!ts.getJSDocTags(node).find(t => t.tagName.text === 'internal'); + } + public rewriteVisibilities() { const visited: Set = new Set(); for (const kvp of this._symbolLookup) { diff --git a/src.csharp/AlphaTab.Test/VisualTests/VisualTestHelper.cs b/src.csharp/AlphaTab.Test/VisualTests/VisualTestHelper.cs index 663212432..b71059892 100644 --- a/src.csharp/AlphaTab.Test/VisualTests/VisualTestHelper.cs +++ b/src.csharp/AlphaTab.Test/VisualTests/VisualTestHelper.cs @@ -16,7 +16,7 @@ namespace AlphaTab.VisualTests partial class VisualTestHelper { public static async Task RunVisualTest(string inputFile, Settings? settings = null, - IList? tracks = null, string? message = null, double tolerancePercent = 1) + IList? tracks = null, string? message = null, double tolerancePercent = 1, bool triggerResize = false) { try { @@ -27,7 +27,7 @@ public static async Task RunVisualTest(string inputFile, Settings? settings = nu var score = ScoreLoader.LoadScoreFromBytes(inputFileData, settings); await RunVisualTestScore(score, referenceFileName, settings, - tracks, message, tolerancePercent); + tracks, message, tolerancePercent, triggerResize); } catch (Exception e) { @@ -58,7 +58,7 @@ await RunVisualTestScore(score, referenceFileName, settings, public static async Task RunVisualTestScore(Score score, string referenceFileName, Settings? settings = null, - IList? tracks = null, string? message = null, double tolerancePercent = 1) + IList? tracks = null, string? message = null, double tolerancePercent = 1, bool triggerResize = false) { settings ??= new Settings(); tracks ??= new Core.List {0}; @@ -92,13 +92,19 @@ public static async Task RunVisualTestScore(Score score, string referenceFileNam var result = new AlphaTab.Core.List(); var totalWidth = 0.0; var totalHeight = 0.0; + var isResizeRender = false; var task = new TaskCompletionSource(); var renderer = new ScoreRenderer(settings) { Width = 1300 }; - + renderer.PreRender.On(isResize => + { + result = new AlphaTab.Core.List(); + totalWidth = 0.0; + totalHeight = 0.0; + }); renderer.PartialRenderFinished.On(e => { if (e != null) @@ -111,7 +117,15 @@ public static async Task RunVisualTestScore(Score score, string referenceFileNam totalWidth = e.TotalWidth; totalHeight = e.TotalHeight; result.Add(e); - task.SetResult(null); + if(!triggerResize || isResizeRender) + { + task.SetResult(null); + } + else if(triggerResize) + { + isResizeRender = true; + renderer.ResizeRender(); + } }); renderer.Error.On((e) => { task.SetException(e); }); diff --git a/src.kotlin/alphaTab/src/jvmTest/kotlin/alphaTab/visualTests/VisualTestHelperPartials.kt b/src.kotlin/alphaTab/src/jvmTest/kotlin/alphaTab/visualTests/VisualTestHelperPartials.kt index a06ba17be..8ebb2fe56 100644 --- a/src.kotlin/alphaTab/src/jvmTest/kotlin/alphaTab/visualTests/VisualTestHelperPartials.kt +++ b/src.kotlin/alphaTab/src/jvmTest/kotlin/alphaTab/visualTests/VisualTestHelperPartials.kt @@ -32,7 +32,8 @@ class VisualTestHelperPartials { settings: Settings? = null, tracks: MutableList? = null, message: String? = null, - tolerancePercent: Double = 1.0 + tolerancePercent: Double = 1.0, + triggerResize: Boolean = false ) { try { val fullInputFile = "test-data/visual-tests/$inputFile" @@ -46,7 +47,8 @@ class VisualTestHelperPartials { settings, tracks, message, - tolerancePercent + tolerancePercent, + triggerResize ) } catch (e: Throwable) { Assert.fail("Failed to run visual test $e") @@ -86,7 +88,8 @@ class VisualTestHelperPartials { settings: Settings? = null, tracks: MutableList? = null, message: String? = null, - tolerancePercent: Double = 1.0 + tolerancePercent: Double = 1.0, + triggerResize: Boolean = false ) { val actualSettings = settings ?: Settings() val actualTracks = tracks ?: ArrayList() @@ -116,9 +119,10 @@ class VisualTestHelperPartials { val referenceFileData = TestPlatformPartials.loadFile(actualReferenceFileName) - val result = ArrayList() + var result = ArrayList() var totalWidth = 0.0 var totalHeight = 0.0 + var isResizeRender = false val renderer = ScoreRenderer(actualSettings) renderer.width = 1300.0 @@ -128,6 +132,11 @@ class VisualTestHelperPartials { var error: Throwable? = null + renderer.preRender.on { _ -> + result = ArrayList() + totalWidth = 0.0 + totalHeight = 0.0 + } renderer.partialRenderFinished.on { e -> result.add(e) } @@ -135,7 +144,13 @@ class VisualTestHelperPartials { totalWidth = e.totalWidth totalHeight = e.totalHeight result.add(e) - waitHandle.release() + if(!triggerResize || isResizeRender) { + waitHandle.release() + } else if(triggerResize) { + isResizeRender = true + renderer.resizeRender() + } + } renderer.error.on { e -> error = e diff --git a/src/AlphaTabApiBase.ts b/src/AlphaTabApiBase.ts index a9e7223a0..2646c4305 100644 --- a/src/AlphaTabApiBase.ts +++ b/src/AlphaTabApiBase.ts @@ -289,6 +289,9 @@ export class AlphaTabApiBase { } } + /** + * @internal + */ private triggerResize(): void { if (!this.container.isVisible) { Logger.warning( @@ -334,6 +337,7 @@ export class AlphaTabApiBase { public tex(tex: string, tracks?: number[]): void { try { let parser: AlphaTexImporter = new AlphaTexImporter(); + parser.logErrors = true; parser.initFromString(tex, this.settings); let score: Score = parser.readScore(); this.renderScore(score, tracks); diff --git a/src/importer/AlphaTexImporter.ts b/src/importer/AlphaTexImporter.ts index 7568b8e65..b9e23cd5c 100644 --- a/src/importer/AlphaTexImporter.ts +++ b/src/importer/AlphaTexImporter.ts @@ -125,6 +125,8 @@ export class AlphaTexImporter extends ScoreImporter { private _staffHasExplicitTuning: boolean = false; private _staffTuningApplied: boolean = false; + public logErrors:boolean = false; + public constructor() { super(); } @@ -197,13 +199,17 @@ export class AlphaTexImporter extends ScoreImporter { } else { e = AlphaTexError.symbolError(this._curChPos, nonterm, expected, expected, this._syData); } - Logger.error(this.name, e.message!); + if(this.logErrors) { + Logger.error(this.name, e.message!); + } throw e; } private errorMessage(message: string): void { let e: AlphaTexError = AlphaTexError.errorMessage(this._curChPos, message); - Logger.error(this.name, e.message!); + if(this.logErrors) { + Logger.error(this.name, e.message!); + } throw e; } diff --git a/src/rendering/ScoreRenderer.ts b/src/rendering/ScoreRenderer.ts index 2836d1a0b..65c931aca 100644 --- a/src/rendering/ScoreRenderer.ts +++ b/src/rendering/ScoreRenderer.ts @@ -126,7 +126,6 @@ export class ScoreRenderer implements IScoreRenderer { (this.preRender as EventEmitterOfT).trigger(false); this.recreateLayout(); this.layoutAndRender(); - this._renderedTracks = this.tracks; Logger.debug('Rendering', 'Rendering finished'); } @@ -157,6 +156,7 @@ export class ScoreRenderer implements IScoreRenderer { ); this.layout!.layoutAndRender(); this.layout!.renderAnnotation(); + this._renderedTracks = this.tracks; this.onRenderFinished(); (this.postRenderFinished as EventEmitter).trigger(); } diff --git a/src/rendering/glyphs/BeatContainerGlyph.ts b/src/rendering/glyphs/BeatContainerGlyph.ts index 472f886a7..5d9308929 100644 --- a/src/rendering/glyphs/BeatContainerGlyph.ts +++ b/src/rendering/glyphs/BeatContainerGlyph.ts @@ -35,12 +35,12 @@ export class BeatContainerGlyph extends Glyph { } public registerLayoutingInfo(layoutings: BarLayoutingInfo): void { - let preBeatStretch: number = this.onTimeX; + let preBeatStretch: number = this.preNotes.computedWidth + this.onNotes.centerX; if(this.beat.graceGroup && !this.beat.graceGroup.isComplete) { preBeatStretch += BeatContainerGlyph.GraceBeatPadding * this.renderer.scale; } - let postBeatStretch: number = this.onNotes.width - this.onNotes.centerX; + let postBeatStretch: number = this.onNotes.computedWidth - this.onNotes.centerX; // make space for flag const helper = this.renderer.helpers.getBeamingHelperForBeat(this.beat); if(helper && helper.hasFlag || this.beat.graceType !== GraceType.None) { @@ -57,8 +57,8 @@ export class BeatContainerGlyph extends Glyph { layoutings.addBeatSpring(this.beat, preBeatStretch, postBeatStretch); // store sizes for special renderers like the EffectBarRenderer - layoutings.setPreBeatSize(this.beat, this.preNotes.width); - layoutings.setOnBeatSize(this.beat, this.onNotes.width); + layoutings.setPreBeatSize(this.beat, this.preNotes.computedWidth); + layoutings.setOnBeatSize(this.beat, this.onNotes.computedWidth); layoutings.setBeatCenterX(this.beat, this.onNotes.centerX); } diff --git a/src/rendering/glyphs/BeatGlyphBase.ts b/src/rendering/glyphs/BeatGlyphBase.ts index 333a93940..958c953f2 100644 --- a/src/rendering/glyphs/BeatGlyphBase.ts +++ b/src/rendering/glyphs/BeatGlyphBase.ts @@ -5,6 +5,7 @@ import { GlyphGroup } from '@src/rendering/glyphs/GlyphGroup'; export class BeatGlyphBase extends GlyphGroup { public container!: BeatContainerGlyph; + public computedWidth: number = 0; public constructor() { super(0, 0); @@ -23,6 +24,7 @@ export class BeatGlyphBase extends GlyphGroup { } } this.width = w; + this.computedWidth = w; } protected noteLoop(action: (note: Note) => void): void { diff --git a/src/rendering/glyphs/TabBeatGlyph.ts b/src/rendering/glyphs/TabBeatGlyph.ts index e212bf1f4..8e4e90628 100644 --- a/src/rendering/glyphs/TabBeatGlyph.ts +++ b/src/rendering/glyphs/TabBeatGlyph.ts @@ -126,6 +126,7 @@ export class TabBeatGlyph extends BeatOnNoteGlyphBase { w += g.width; } this.width = w; + this.computedWidth = w; if (this.container.beat.isEmpty) { this.centerX = this.width / 2; } else if (this.container.beat.isRest) { diff --git a/test-data/visual-tests/music-notation/beams-advanced.png b/test-data/visual-tests/music-notation/beams-advanced.png index af34fc4aa..29591da4f 100644 Binary files a/test-data/visual-tests/music-notation/beams-advanced.png and b/test-data/visual-tests/music-notation/beams-advanced.png differ diff --git a/test/visualTests/VisualTestHelper.ts b/test/visualTests/VisualTestHelper.ts index a28e99c13..cb8c7628d 100644 --- a/test/visualTests/VisualTestHelper.ts +++ b/test/visualTests/VisualTestHelper.ts @@ -24,7 +24,8 @@ export class VisualTestHelper { settings?: Settings, tracks?: number[], message?: string, - tolerancePercent: number = 1 + tolerancePercent: number = 1, + triggerResize: boolean = false ): Promise { try { const inputFileData = await TestPlatform.loadFile(`test-data/visual-tests/${inputFile}`); @@ -37,7 +38,8 @@ export class VisualTestHelper { settings, tracks, message, - tolerancePercent + tolerancePercent, + triggerResize ); } catch (e) { fail(`Failed to run visual test ${e}`); @@ -82,7 +84,7 @@ export class VisualTestHelper { * @partial */ private static async loadFonts(): Promise { - if(VisualTestHelper._fontsLoaded) { + if (VisualTestHelper._fontsLoaded) { return; } VisualTestHelper._fontsLoaded = true; @@ -140,7 +142,7 @@ export class VisualTestHelper { await Promise.all(promises); - for(const font of allFonts) { + for (const font of allFonts) { document.fonts.add(font); } } @@ -155,7 +157,8 @@ export class VisualTestHelper { settings?: Settings, tracks?: number[], message?: string, - tolerancePercent: number = 1 + tolerancePercent: number = 1, + triggerResize: boolean = false ): Promise { try { if (!settings) { @@ -222,8 +225,14 @@ export class VisualTestHelper { let result: RenderFinishedEventArgs[] = []; let totalWidth: number = 0; let totalHeight: number = 0; + let isResizeRender = false; let render = new Promise((resolve, reject) => { const api = new AlphaTabApi(renderElement, settings); + api.renderStarted.on(isResize => { + result = []; + totalWidth = 0; + totalHeight = 0; + }); api.renderer.partialRenderFinished.on(e => { if (e) { result.push(e); @@ -233,7 +242,14 @@ export class VisualTestHelper { totalWidth = e.totalWidth; totalHeight = e.totalHeight; result.push(e); - resolve(); + + if (!triggerResize || isResizeRender) { + resolve(); + } else if(triggerResize) { + isResizeRender = true; + // @ts-ignore + api.triggerResize(); + } }); api.error.on(e => { reject(`Failed to render image: ${e}`); @@ -459,7 +475,7 @@ export class VisualTestHelper { let totalPixels = match.totalPixels - match.transparentPixels; let percentDifference = (match.differentPixels / totalPixels) * 100; result.pass = percentDifference < tolerancePercent; - // result.pass = match.differentPixels < 5; + // result.pass = match.differentPixels === 0; if (!result.pass) { let percentDifferenceText = percentDifference.toFixed(2); diff --git a/test/visualTests/features/SpecialNotes.test.ts b/test/visualTests/features/SpecialNotes.test.ts index 8b6d836d0..a1fe8a897 100644 --- a/test/visualTests/features/SpecialNotes.test.ts +++ b/test/visualTests/features/SpecialNotes.test.ts @@ -13,6 +13,13 @@ describe('SpecialNotesTests', () => { await VisualTestHelper.runVisualTest('special-notes/grace-notes-advanced.gp', undefined, [0, 1]); }); + it('grace-resize', async () => { + // grace resize regression: we have the repeating issue that + // grace notes flick around to wrong positions during resizes + // due to wrong size registrations. (#604) + await VisualTestHelper.runVisualTest('special-notes/grace-notes-advanced.gp', undefined, [0, 1], undefined, 1, true); + }); + it('dead-notes', async () => { await VisualTestHelper.runVisualTest('special-notes/dead-notes.gp'); });