diff --git a/packages/alphatab/src/DisplaySettings.ts b/packages/alphatab/src/DisplaySettings.ts index 4fce2772d..52198ecd2 100644 --- a/packages/alphatab/src/DisplaySettings.ts +++ b/packages/alphatab/src/DisplaySettings.ts @@ -248,6 +248,14 @@ export class DisplaySettings { */ public accoladeBarPaddingRight: number = 3; + /** + * The padding between inline tuning labels and the start of the tab staff. + * @since 1.9.0 + * @category Display + * @defaultValue `5` + */ + public inlineTuningPaddingRight: number = 5; + // Staff padding /** diff --git a/packages/alphatab/src/generated/DisplaySettingsJson.ts b/packages/alphatab/src/generated/DisplaySettingsJson.ts index 20cb54ddb..4a9a493b4 100644 --- a/packages/alphatab/src/generated/DisplaySettingsJson.ts +++ b/packages/alphatab/src/generated/DisplaySettingsJson.ts @@ -218,6 +218,13 @@ export interface DisplaySettingsJson { * @defaultValue `3` */ accoladeBarPaddingRight?: number; + /** + * The padding between inline tuning labels and the start of the tab staff. + * @since 1.9.0 + * @category Display + * @defaultValue `5` + */ + inlineTuningPaddingRight?: number; /** * The top padding applied to the first main notation staff (standard, tabs, numbered, slash). * @since 1.8.0 diff --git a/packages/alphatab/src/generated/DisplaySettingsSerializer.ts b/packages/alphatab/src/generated/DisplaySettingsSerializer.ts index c07b064b7..d715e8f1e 100644 --- a/packages/alphatab/src/generated/DisplaySettingsSerializer.ts +++ b/packages/alphatab/src/generated/DisplaySettingsSerializer.ts @@ -42,6 +42,7 @@ export class DisplaySettingsSerializer { o.set("systemlabelpaddingleft", obj.systemLabelPaddingLeft); o.set("systemlabelpaddingright", obj.systemLabelPaddingRight); o.set("accoladebarpaddingright", obj.accoladeBarPaddingRight); + o.set("inlinetuningpaddingright", obj.inlineTuningPaddingRight); o.set("firstnotationstaffpaddingtop", obj.firstNotationStaffPaddingTop); o.set("lastnotationstaffpaddingbottom", obj.lastNotationStaffPaddingBottom); o.set("notationstaffpaddingtop", obj.notationStaffPaddingTop); @@ -109,6 +110,9 @@ export class DisplaySettingsSerializer { case "accoladebarpaddingright": obj.accoladeBarPaddingRight = v! as number; return true; + case "inlinetuningpaddingright": + obj.inlineTuningPaddingRight = v! as number; + return true; case "firstnotationstaffpaddingtop": obj.firstNotationStaffPaddingTop = v! as number; return true; diff --git a/packages/alphatab/src/generated/model/RenderStylesheetSerializer.ts b/packages/alphatab/src/generated/model/RenderStylesheetSerializer.ts index 10ac9a9ac..1f3a17c33 100644 --- a/packages/alphatab/src/generated/model/RenderStylesheetSerializer.ts +++ b/packages/alphatab/src/generated/model/RenderStylesheetSerializer.ts @@ -6,6 +6,7 @@ import { RenderStylesheet } from "@coderline/alphatab/model/RenderStylesheet"; import { JsonHelper } from "@coderline/alphatab/io/JsonHelper"; import { BracketExtendMode } from "@coderline/alphatab/model/RenderStylesheet"; +import { TuningDisplayMode } from "@coderline/alphatab/model/RenderStylesheet"; import { TrackNamePolicy } from "@coderline/alphatab/model/RenderStylesheet"; import { TrackNameMode } from "@coderline/alphatab/model/RenderStylesheet"; import { TrackNameOrientation } from "@coderline/alphatab/model/RenderStylesheet"; @@ -29,6 +30,7 @@ export class RenderStylesheetSerializer { o.set("bracketextendmode", obj.bracketExtendMode as number); o.set("usesystemsignseparator", obj.useSystemSignSeparator); o.set("globaldisplaytuning", obj.globalDisplayTuning); + o.set("tuningdisplaymode", obj.tuningDisplayMode as number); if (obj.perTrackDisplayTuning !== null) { const m = new Map(); o.set("pertrackdisplaytuning", m); @@ -80,6 +82,9 @@ export class RenderStylesheetSerializer { case "globaldisplaytuning": obj.globalDisplayTuning = v! as boolean; return true; + case "tuningdisplaymode": + obj.tuningDisplayMode = JsonHelper.parseEnum(v, TuningDisplayMode)!; + return true; case "pertrackdisplaytuning": obj.perTrackDisplayTuning = new Map(); JsonHelper.forEach(v, (v, k) => { diff --git a/packages/alphatab/src/importer/alphaTex/AlphaTex1EnumMappings.ts b/packages/alphatab/src/importer/alphaTex/AlphaTex1EnumMappings.ts index 598cad9ef..a3fe7fab7 100644 --- a/packages/alphatab/src/importer/alphaTex/AlphaTex1EnumMappings.ts +++ b/packages/alphatab/src/importer/alphaTex/AlphaTex1EnumMappings.ts @@ -18,7 +18,8 @@ import type { BracketExtendMode, TrackNameMode, TrackNameOrientation, - TrackNamePolicy + TrackNamePolicy, + TuningDisplayMode } from '@coderline/alphatab/model/RenderStylesheet'; import type { SimileMark } from '@coderline/alphatab/model/SimileMark'; import type { TremoloPickingStyle } from '@coderline/alphatab/model/TremoloPickingEffect'; @@ -198,6 +199,13 @@ export class AlphaTex1EnumMappings { ['shortname', 1] ]); public static readonly trackNameModeReversed = AlphaTex1EnumMappings._reverse(AlphaTex1EnumMappings.trackNameMode); + public static readonly tuningDisplayMode = new Map([ + ['score', 0], + ['staff', 1] + ]); + public static readonly tuningDisplayModeReversed = AlphaTex1EnumMappings._reverse( + AlphaTex1EnumMappings.tuningDisplayMode + ); public static readonly textAlign = new Map([ ['left', 0], ['center', 1], diff --git a/packages/alphatab/src/importer/alphaTex/AlphaTex1LanguageDefinitions.ts b/packages/alphatab/src/importer/alphaTex/AlphaTex1LanguageDefinitions.ts index 8e84fe7ed..520fbf79f 100644 --- a/packages/alphatab/src/importer/alphaTex/AlphaTex1LanguageDefinitions.ts +++ b/packages/alphatab/src/importer/alphaTex/AlphaTex1LanguageDefinitions.ts @@ -181,6 +181,7 @@ export class AlphaTex1LanguageDefinitions { ['showdynamics', null], ['hidedynamics', null], ['usesystemsignseparator', null], + ['tuningdisplaymode', [[[[10, 17], 0, ['score', 'staff']]]]], ['multibarrest', null], ['bracketextendmode', [[[[10, 17], 0, ['nobrackets', 'groupstaves', 'groupsimilarinstruments']]]]], ['singletracktracknamepolicy', [[[[10, 17], 0, ['hidden', 'firstsystem', 'allsystems']]]]], @@ -534,6 +535,7 @@ export class AlphaTex1LanguageDefinitions { ['showdynamics', null], ['hidedynamics', null], ['usesystemsignseparator', null], + ['tuningdisplaymode', null], ['multibarrest', null], ['bracketextendmode', null], ['singletracktracknamepolicy', null], diff --git a/packages/alphatab/src/importer/alphaTex/AlphaTex1LanguageHandler.ts b/packages/alphatab/src/importer/alphaTex/AlphaTex1LanguageHandler.ts index 4b38647d0..1608567fa 100644 --- a/packages/alphatab/src/importer/alphaTex/AlphaTex1LanguageHandler.ts +++ b/packages/alphatab/src/importer/alphaTex/AlphaTex1LanguageHandler.ts @@ -71,7 +71,7 @@ import { NoteOrnament } from '@coderline/alphatab/model/NoteOrnament'; import { Ottavia } from '@coderline/alphatab/model/Ottavia'; import { PercussionMapper } from '@coderline/alphatab/model/PercussionMapper'; import { PickStroke } from '@coderline/alphatab/model/PickStroke'; -import { BarNumberDisplay, type RenderStylesheet } from '@coderline/alphatab/model/RenderStylesheet'; +import { BarNumberDisplay, type RenderStylesheet, TuningDisplayMode } from '@coderline/alphatab/model/RenderStylesheet'; import { HeaderFooterStyle, Score, ScoreStyle, ScoreSubElement } from '@coderline/alphatab/model/Score'; import { Section } from '@coderline/alphatab/model/Section'; import { SimileMark } from '@coderline/alphatab/model/SimileMark'; @@ -188,6 +188,18 @@ export class AlphaTex1LanguageHandler implements IAlphaTexLanguageImportHandler case 'usesystemsignseparator': score.stylesheet.useSystemSignSeparator = true; return ApplyNodeResult.Applied; + case 'tuningdisplaymode': + const tuningDisplayMode = AlphaTex1LanguageHandler._parseEnumValue( + importer, + metaData.arguments!, + 'tuning display mode', + AlphaTex1EnumMappings.tuningDisplayMode + ); + if (tuningDisplayMode === undefined) { + return ApplyNodeResult.NotAppliedSemanticError; + } + score.stylesheet.tuningDisplayMode = tuningDisplayMode!; + return ApplyNodeResult.Applied; case 'multibarrest': score.stylesheet.multiTrackMultiBarRest = true; return ApplyNodeResult.Applied; @@ -2562,6 +2574,14 @@ export class AlphaTex1LanguageHandler implements IAlphaTexLanguageImportHandler if (stylesheet.useSystemSignSeparator) { nodes.push(Atnf.meta('useSystemSignSeparator')); } + if (stylesheet.tuningDisplayMode !== TuningDisplayMode.Score) { + nodes.push( + Atnf.identMeta( + 'tuningDisplayMode', + AlphaTex1EnumMappings.tuningDisplayModeReversed.get(stylesheet.tuningDisplayMode)! + ) + ); + } if (stylesheet.multiTrackMultiBarRest) { nodes.push(Atnf.meta('multiBarRest')); } diff --git a/packages/alphatab/src/model/RenderStylesheet.ts b/packages/alphatab/src/model/RenderStylesheet.ts index 81e4a4ad5..a0ad180f2 100644 --- a/packages/alphatab/src/model/RenderStylesheet.ts +++ b/packages/alphatab/src/model/RenderStylesheet.ts @@ -72,6 +72,22 @@ export enum TrackNameOrientation { Vertical = 1 } +/** + * Lists the different places where string tuning information is displayed. + * @public + */ +export enum TuningDisplayMode { + /** + * Tuning information is displayed above the score. + */ + Score = 0, + + /** + * Tuning note names are displayed beside the corresponding tab staff lines. + */ + Staff = 1 +} + /** * How bar numbers are displayed * @public @@ -119,6 +135,11 @@ export class RenderStylesheet { */ public globalDisplayTuning: boolean = true; + /** + * The place where tuning information is displayed. + */ + public tuningDisplayMode: TuningDisplayMode = TuningDisplayMode.Score; + /** * Whether to show the tuning.(per-track) */ diff --git a/packages/alphatab/src/model/_barrel.ts b/packages/alphatab/src/model/_barrel.ts index c5c0e1a6a..94dea8678 100644 --- a/packages/alphatab/src/model/_barrel.ts +++ b/packages/alphatab/src/model/_barrel.ts @@ -51,6 +51,7 @@ export { TrackNamePolicy, TrackNameMode, TrackNameOrientation, + TuningDisplayMode, BarNumberDisplay } from '@coderline/alphatab/model/RenderStylesheet'; export { RepeatGroup } from '@coderline/alphatab/model/RepeatGroup'; diff --git a/packages/alphatab/src/rendering/glyphs/InlineTuningGlyph.ts b/packages/alphatab/src/rendering/glyphs/InlineTuningGlyph.ts new file mode 100644 index 000000000..6310e2024 --- /dev/null +++ b/packages/alphatab/src/rendering/glyphs/InlineTuningGlyph.ts @@ -0,0 +1,72 @@ +import { Tuning } from '@coderline/alphatab/model/Tuning'; +import { TrackSubElement } from '@coderline/alphatab/model/Track'; +import { NotationElement } from '@coderline/alphatab/NotationSettings'; +import { type ICanvas, TextAlign, TextBaseline } from '@coderline/alphatab/platform/ICanvas'; +import { TabBarRenderer } from '@coderline/alphatab/rendering/TabBarRenderer'; +import { Glyph } from '@coderline/alphatab/rendering/glyphs/Glyph'; +import type { RenderStaff } from '@coderline/alphatab/rendering/staves/RenderStaff'; +import { ElementStyleHelper } from '@coderline/alphatab/rendering/utils/ElementStyleHelper'; + +/** + * @internal + */ +export class InlineTuningGlyph extends Glyph { + public readonly staff: RenderStaff; + + private readonly _tabRenderer: TabBarRenderer; + private readonly _tunings: number[]; + + public constructor(staff: RenderStaff, tabRenderer: TabBarRenderer) { + super(0, 0); + this.staff = staff; + this._tabRenderer = tabRenderer; + this._tunings = staff.modelStaff.stringTuning.tunings; + this.renderer = tabRenderer; + } + + public override doLayout(): void { + const canvas = this.renderer.scoreRenderer.canvas!; + const oldFont = canvas.font; + canvas.font = this.renderer.resources.elementFonts.get(NotationElement.GuitarTuning)!; + + let textWidth = 0; + for (const tuning of this._tunings) { + textWidth = Math.max(textWidth, canvas.measureText(Tuning.getTextForTuning(tuning, false)).width); + } + + canvas.font = oldFont; + + this.width = textWidth > 0 ? textWidth + this.renderer.settings.display.inlineTuningPaddingRight : 0; + this.height = this._tabRenderer.height; + } + + public override paint(cx: number, cy: number, canvas: ICanvas): void { + if (this.width === 0) { + return; + } + + const oldFont = canvas.font; + const oldBaseLine = canvas.textBaseline; + const oldTextAlign = canvas.textAlign; + + canvas.font = this.renderer.resources.elementFonts.get(NotationElement.GuitarTuning)!; + canvas.textBaseline = TextBaseline.Middle; + canvas.textAlign = TextAlign.Right; + + const textEndX = cx - this.renderer.settings.display.inlineTuningPaddingRight; + + using _ = ElementStyleHelper.track(canvas, TrackSubElement.StringTuning, this.staff.modelStaff.track, true); + + for (let i = 0, j = this._tunings.length; i < j; i++) { + canvas.fillText( + Tuning.getTextForTuning(this._tunings[i], false), + textEndX, + cy + this._tabRenderer.y + this._tabRenderer.getLineY(i) + ); + } + + canvas.font = oldFont; + canvas.textBaseline = oldBaseLine; + canvas.textAlign = oldTextAlign; + } +} diff --git a/packages/alphatab/src/rendering/layout/ScoreLayout.ts b/packages/alphatab/src/rendering/layout/ScoreLayout.ts index cc6585301..b821d1435 100644 --- a/packages/alphatab/src/rendering/layout/ScoreLayout.ts +++ b/packages/alphatab/src/rendering/layout/ScoreLayout.ts @@ -4,6 +4,7 @@ import { Logger } from '@coderline/alphatab/Logger'; import type { Bar } from '@coderline/alphatab/model/Bar'; import { Font, FontStyle, FontWeight } from '@coderline/alphatab/model/Font'; import { ModelUtils } from '@coderline/alphatab/model/ModelUtils'; +import { TuningDisplayMode } from '@coderline/alphatab/model/RenderStylesheet'; import { type Score, ScoreStyle, ScoreSubElement } from '@coderline/alphatab/model/Score'; import type { Staff } from '@coderline/alphatab/model/Staff'; import { type Track, TrackSubElement } from '@coderline/alphatab/model/Track'; @@ -305,7 +306,11 @@ export abstract class ScoreLayout { } } // tuning info - if (stavesWithTuning.length > 0 && score.stylesheet.globalDisplayTuning) { + if ( + stavesWithTuning.length > 0 && + score.stylesheet.globalDisplayTuning && + score.stylesheet.tuningDisplayMode === TuningDisplayMode.Score + ) { this.tuningGlyph = new TuningContainerGlyph(0, 0); this.tuningGlyph.renderer = fakeBarRenderer; for (const staff of stavesWithTuning) { diff --git a/packages/alphatab/src/rendering/staves/StaffSystem.ts b/packages/alphatab/src/rendering/staves/StaffSystem.ts index 1fa922a1f..81cec2156 100644 --- a/packages/alphatab/src/rendering/staves/StaffSystem.ts +++ b/packages/alphatab/src/rendering/staves/StaffSystem.ts @@ -6,7 +6,8 @@ import { BracketExtendMode, TrackNameMode, TrackNameOrientation, - TrackNamePolicy + TrackNamePolicy, + TuningDisplayMode } from '@coderline/alphatab/model/RenderStylesheet'; import { type Track, TrackSubElement } from '@coderline/alphatab/model/Track'; import { NotationElement } from '@coderline/alphatab/NotationSettings'; @@ -15,6 +16,8 @@ import type { RenderingResources } from '@coderline/alphatab/RenderingResources' import type { BarRendererBase } from '@coderline/alphatab/rendering/BarRendererBase'; import type { LineBarRenderer } from '@coderline/alphatab/rendering/LineBarRenderer'; import type { ScoreLayout } from '@coderline/alphatab/rendering/layout/ScoreLayout'; +import { TabBarRenderer } from '@coderline/alphatab/rendering/TabBarRenderer'; +import { InlineTuningGlyph } from '@coderline/alphatab/rendering/glyphs/InlineTuningGlyph'; import { BarLayoutingInfo } from '@coderline/alphatab/rendering/staves/BarLayoutingInfo'; import { MasterBarsRenderers } from '@coderline/alphatab/rendering/staves/MasterBarsRenderers'; import type { RenderStaff } from '@coderline/alphatab/rendering/staves/RenderStaff'; @@ -158,6 +161,8 @@ export class StaffSystem { private _brackets: SystemBracket[] = []; private _staffToBracket = new Map(); + private _inlineTuningGlyphs: InlineTuningGlyph[] = []; + private _inlineTuningWidth = 0; private _contentHeight = 0; private _hasSystemSeparator = false; @@ -657,6 +662,9 @@ export class StaffSystem { } } + this._createInlineTuningGlyphs(); + this.accoladeWidth += this._inlineTuningWidth; + // NOTE: we have a chicken-egg problem when it comes to scaling braces which we try to mitigate here: // - The brace scales with the height of the system // - The height of the system depends on the bars which can be fitted @@ -696,6 +704,82 @@ export class StaffSystem { } } + private _createInlineTuningGlyphs(): void { + this._inlineTuningGlyphs = []; + this._inlineTuningWidth = 0; + + const score = this.layout.renderer.score!; + if ( + this.index !== 0 || + !this.layout.renderer.settings.notation.isNotationElementVisible(NotationElement.GuitarTuning) || + !score.stylesheet.globalDisplayTuning || + score.stylesheet.tuningDisplayMode !== TuningDisplayMode.Staff + ) { + return; + } + + for (const staff of this.allStaves) { + if (!this._shouldCreateInlineTuningGlyph(staff)) { + continue; + } + + const renderer = staff.barRenderers[0]; + if (!(renderer instanceof TabBarRenderer)) { + continue; + } + + const glyph = new InlineTuningGlyph(staff, renderer); + glyph.doLayout(); + this._inlineTuningGlyphs.push(glyph); + this._inlineTuningWidth = Math.max(this._inlineTuningWidth, glyph.width); + } + } + + private _shouldCreateInlineTuningGlyph(staff: RenderStaff): boolean { + const score = this.layout.renderer.score!; + if ( + !staff.isVisible || + staff.staffId !== TabBarRenderer.StaffId + ) { + return false; + } + + const modelStaff = staff.modelStaff; + if ( + modelStaff.isPercussion || + !modelStaff.isStringed || + !modelStaff.showTablature || + modelStaff.stringTuning.tunings.length === 0 + ) { + return false; + } + + const perTrackDisplayTuning = score.stylesheet.perTrackDisplayTuning; + return ( + !perTrackDisplayTuning || + !perTrackDisplayTuning.has(modelStaff.track.index) || + perTrackDisplayTuning.get(modelStaff.track.index) !== false + ); + } + + private _getInlineTuningWidthForTrackGroup(group: StaffTrackGroup): number { + return this._getInlineTuningWidth(glyph => glyph.staff.staffTrackGroup === group); + } + + private _getInlineTuningWidthForBracket(bracket: SystemBracket): number { + return this._getInlineTuningWidth(glyph => bracket.includesStaff(glyph.staff)); + } + + private _getInlineTuningWidth(includeGlyph: (glyph: InlineTuningGlyph) => boolean): number { + let width = 0; + for (const glyph of this._inlineTuningGlyphs) { + if (includeGlyph(glyph)) { + width = Math.max(width, glyph.width); + } + } + return width; + } + private _getStaffTrackGroup(track: Track): StaffTrackGroup | null { for (let i: number = 0, j: number = this.staves.length; i < j; i++) { const g: StaffTrackGroup = this.staves[i]; @@ -874,6 +958,7 @@ export class StaffSystem { g.staves[0].x - // left side of the bracket settings.display.accoladeBarPaddingRight - + this._getInlineTuningWidthForTrackGroup(g) - (g.bracket?.width ?? 0) - // padding between label and bracket settings.display.systemLabelPaddingRight; @@ -909,6 +994,8 @@ export class StaffSystem { } } + this._paintInlineTunings(cx, cy, canvas); + const needsSystemBarLine = !this.layout.renderer.score!.stylesheet.extendBarLines; if (this.allStaves.length > 0 && needsSystemBarLine) { let previousStaffInBracket: RenderStaff | null = null; @@ -944,6 +1031,12 @@ export class StaffSystem { } } + private _paintInlineTunings(cx: number, cy: number, canvas: ICanvas): void { + for (const glyph of this._inlineTuningGlyphs) { + glyph.paint(cx + glyph.staff.x, cy + glyph.staff.y, canvas); + } + } + private _paintBrackets(cx: number, cy: number, canvas: ICanvas) { const settings = this.layout.renderer.settings; @@ -951,7 +1044,8 @@ export class StaffSystem { if (bracket.canPaint) { const barStartX: number = cx + bracket.firstVisibleStaffInBracket!.x; const barSize: number = bracket.width; - const barOffset: number = settings.display.accoladeBarPaddingRight; + const barOffset: number = + settings.display.accoladeBarPaddingRight + this._getInlineTuningWidthForBracket(bracket); const firstStart: number = cy + bracket.firstVisibleStaffInBracket!.contentTop; const lastEnd: number = cy + bracket.lastVisibleStaffInBracket!.contentBottom; let accoladeStart: number = firstStart; diff --git a/packages/alphatab/test-data/visual-tests/layout/inline-tuning-first-system.png b/packages/alphatab/test-data/visual-tests/layout/inline-tuning-first-system.png new file mode 100644 index 000000000..66862796f Binary files /dev/null and b/packages/alphatab/test-data/visual-tests/layout/inline-tuning-first-system.png differ diff --git a/packages/alphatab/test/importer/AlphaTexImporter.test.ts b/packages/alphatab/test/importer/AlphaTexImporter.test.ts index 39a76e468..ace28cc66 100644 --- a/packages/alphatab/test/importer/AlphaTexImporter.test.ts +++ b/packages/alphatab/test/importer/AlphaTexImporter.test.ts @@ -32,7 +32,8 @@ import { BracketExtendMode, TrackNameMode, TrackNameOrientation, - TrackNamePolicy + TrackNamePolicy, + TuningDisplayMode } from '@coderline/alphatab/model/RenderStylesheet'; import { type Score, ScoreSubElement } from '@coderline/alphatab/model/Score'; import { SimileMark } from '@coderline/alphatab/model/SimileMark'; @@ -1856,6 +1857,7 @@ describe('AlphaTexImporterTest', () => { \\hideDynamics \\bracketExtendMode nobrackets \\useSystemSignSeparator + \\tuningDisplayMode staff \\singleTrackTrackNamePolicy allsystems \\multiTrackTrackNamePolicy Hidden \\firstSystemTrackNameMode fullname @@ -1873,6 +1875,7 @@ describe('AlphaTexImporterTest', () => { expect(score.stylesheet.hideDynamics).toBe(true); expect(score.stylesheet.bracketExtendMode).toBe(BracketExtendMode.NoBrackets); expect(score.stylesheet.useSystemSignSeparator).toBe(true); + expect(score.stylesheet.tuningDisplayMode).toBe(TuningDisplayMode.Staff); expect(score.stylesheet.singleTrackTrackNamePolicy).toBe(TrackNamePolicy.AllSystems); expect(score.stylesheet.multiTrackTrackNamePolicy).toBe(TrackNamePolicy.Hidden); expect(score.stylesheet.firstSystemTrackNameMode).toBe(TrackNameMode.FullName); diff --git a/packages/alphatab/test/visualTests/features/Layout.test.ts b/packages/alphatab/test/visualTests/features/Layout.test.ts index c2a12bac7..04e7c44af 100644 --- a/packages/alphatab/test/visualTests/features/Layout.test.ts +++ b/packages/alphatab/test/visualTests/features/Layout.test.ts @@ -124,6 +124,24 @@ describe('LayoutTests', () => { }); }); + it('inline-tuning-first-system', async () => { + const settings: Settings = new Settings(); + settings.display.layoutMode = LayoutMode.Parchment; + await VisualTestHelper.runVisualTestTex( + ` + \\tuningDisplayMode staff + \\track { defaultSystemsLayout 2 } + \\staff { tabs } + 0.6.4 2.6.4 3.6.4 0.5.4 | + 2.5.4 3.5.4 0.4.4 2.4.4 | + 3.4.4 0.3.4 2.3.4 3.3.4 | + 0.2.4 1.2.4 3.2.4 0.1.4 | + `, + 'test-data/visual-tests/layout/inline-tuning-first-system.png', + settings + ); + }); + it('system-layout-tex', async () => { const settings: Settings = new Settings(); settings.display.layoutMode = LayoutMode.Parchment; diff --git a/packages/alphatex/src/definitions.ts b/packages/alphatex/src/definitions.ts index 1e721ca30..14b425f06 100644 --- a/packages/alphatex/src/definitions.ts +++ b/packages/alphatex/src/definitions.ts @@ -43,6 +43,7 @@ import { subtitle } from '@coderline/alphatab-alphatex//metadata/score/subtitle' import { systemsLayout } from '@coderline/alphatab-alphatex//metadata/score/systemslayout'; import { tab } from '@coderline/alphatab-alphatex//metadata/score/tab'; import { title } from '@coderline/alphatab-alphatex//metadata/score/title'; +import { tuningDisplayMode } from '@coderline/alphatab-alphatex//metadata/score/tuningdisplaymode'; import { useSystemSignSeparator } from '@coderline/alphatab-alphatex//metadata/score/usesystemsignseparator'; import { words } from '@coderline/alphatab-alphatex//metadata/score/words'; import { wordsAndMusic } from '@coderline/alphatab-alphatex//metadata/score/wordsandmusic'; @@ -178,6 +179,7 @@ export const scoreMetaData = metadata( showDynamics, hideDynamics, useSystemSignSeparator, + tuningDisplayMode, multiBarRest, bracketExtendMode, singleTrackTrackNamePolicy, diff --git a/packages/alphatex/src/enum.ts b/packages/alphatex/src/enum.ts index 014c0516e..65331f264 100644 --- a/packages/alphatex/src/enum.ts +++ b/packages/alphatex/src/enum.ts @@ -19,6 +19,7 @@ export const alphaTexMappedEnumLookup = { TrackNamePolicy: alphaTab.model.TrackNamePolicy, TrackNameOrientation: alphaTab.model.TrackNameOrientation, TrackNameMode: alphaTab.model.TrackNameMode, + TuningDisplayMode: alphaTab.model.TuningDisplayMode, TextAlign: alphaTab.platform.TextAlign, BendType: alphaTab.model.BendType, KeySignature: alphaTab.model.KeySignature, @@ -231,6 +232,10 @@ export const alphaTexMappedEnumMapping: { FullName: { snippet: 'fullName', shortDescription: 'Short track names (abbreviations) are displayed.' }, ShortName: { snippet: 'shortName', shortDescription: 'Full track names are displayed.' } }, + TuningDisplayMode: { + Score: { snippet: 'score', shortDescription: 'Display tuning information above the score.' }, + Staff: { snippet: 'staff', shortDescription: 'Display tuning note names beside tab staff lines.' } + }, TextAlign: { Left: { snippet: 'left', diff --git a/packages/alphatex/src/metadata/score/tuningdisplaymode.ts b/packages/alphatex/src/metadata/score/tuningdisplaymode.ts new file mode 100644 index 000000000..5fe8276f7 --- /dev/null +++ b/packages/alphatex/src/metadata/score/tuningdisplaymode.ts @@ -0,0 +1,27 @@ +import * as alphaTab from '@coderline/alphatab'; +import { enumParameter } from '@coderline/alphatab-alphatex/enum'; +import type { MetadataTagDefinition } from '@coderline/alphatab-alphatex/types'; + +export const tuningDisplayMode: MetadataTagDefinition = { + tag: '\\tuningDisplayMode', + snippet: '\\tuningDisplayMode ${1:score}$0', + shortDescription: 'Sets where string tuning information is displayed.', + signatures: [ + { + parameters: [ + { + name: 'mode', + shortDescription: 'The mode to use', + parseMode: alphaTab.importer.alphaTex.ArgumentListParseTypesMode.Required, + ...enumParameter('TuningDisplayMode') + } + ] + } + ], + examples: ` + \\tuningDisplayMode staff + \\track "Guitar" + \\staff { score tabs } + 0.6.4 2.6.4 3.6.4 0.5.4 + ` +};