Skip to content

Commit f23e3bd

Browse files
committed
feat(tab): add H/P text labels to hammer-on and pull-off arcs
In the Tab renderer, the arc connecting hammer-on and pull-off notes is now annotated with an "H" (ascending fret = hammer-on) or "P" (descending fret = pull-off) label above the arc midpoint. The label is drawn via an overridden paint() in TabSlurGlyph, reusing the same canvas.fillText path already used for whammy/bend slurText. TieGlyph's coordinate fields (_startX/Y, _endX/Y, _tieHeight, _shouldPaint) are widened from private to protected to allow the subclass to read them during paint. Fixes #2608
1 parent 220858a commit f23e3bd

File tree

3 files changed

+33
-4
lines changed

3 files changed

+33
-4
lines changed

packages/alphatab/src/rendering/glyphs/TabBeatContainerGlyph.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,12 +66,17 @@ export class TabBeatContainerGlyph extends BeatContainerGlyph {
6666
}
6767
}
6868
if (!expanded) {
69+
let slurText: string | undefined = undefined;
70+
if (n.isHammerPullOrigin && n.hammerPullDestination) {
71+
slurText = n.hammerPullDestination.fret >= n.fret ? 'H' : 'P';
72+
}
6973
const effectSlur: TabSlurGlyph = new TabSlurGlyph(
7074
`tab.slur.effect.${n.id}`,
7175
n,
7276
n.effectSlurDestination,
7377
false,
74-
false
78+
false,
79+
slurText
7580
);
7681
this._effectSlurs.push(effectSlur);
7782
this.addTie(effectSlur);

packages/alphatab/src/rendering/glyphs/TabSlurGlyph.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,22 @@ import { BeamDirection } from '@coderline/alphatab/rendering/utils/BeamDirection
77
*/
88
export class TabSlurGlyph extends TabTieGlyph {
99
private _forSlide: boolean;
10+
private readonly _slurText?: string;
1011

11-
public constructor(slurEffectId: string, startNote: Note, endNote: Note, forSlide: boolean, forEnd:boolean) {
12+
public constructor(slurEffectId: string, startNote: Note, endNote: Note, forSlide: boolean, forEnd:boolean, slurText?: string) {
1213
super(slurEffectId, startNote, endNote, forEnd);
1314
this._forSlide = forSlide;
15+
this._slurText = slurText;
1416
}
1517

1618
public override getTieHeight(startX: number, _startY: number, endX: number, _endY: number): number {
1719
return (Math.log(endX - startX + 1) * this.renderer.settings.notation.slurHeight) / 2;
1820
}
1921

22+
protected override getSlurText(): string | undefined {
23+
return this._slurText;
24+
}
25+
2026
public tryExpand(startNote: Note, endNote: Note, forSlide: boolean, forEnd: boolean): boolean {
2127
// same type required
2228
if (this._forSlide !== forSlide) {

packages/alphatab/src/rendering/glyphs/TieGlyph.ts

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,14 +165,16 @@ export abstract class TieGlyph extends Glyph implements ITieGlyph {
165165
return;
166166
}
167167

168+
const isDown = this.tieDirection === BeamDirection.Down;
169+
168170
if (this.shouldDrawBendSlur()) {
169171
TieGlyph.drawBendSlur(
170172
canvas,
171173
cx + this._startX,
172174
cy + this._startY,
173175
cx + this._endX,
174176
cy + this._endY,
175-
this.tieDirection === BeamDirection.Down,
177+
isDown,
176178
this.renderer.smuflMetrics.tieHeight
177179
);
178180
} else {
@@ -183,11 +185,27 @@ export abstract class TieGlyph extends Glyph implements ITieGlyph {
183185
cy + this._startY,
184186
cx + this._endX,
185187
cy + this._endY,
186-
this.tieDirection === BeamDirection.Down,
188+
isDown,
187189
this._tieHeight,
188190
this.renderer.smuflMetrics.tieMidpointThickness
189191
);
190192
}
193+
194+
const slurText = this.getSlurText();
195+
if (slurText) {
196+
const midX = cx + (this._startX + this._endX) / 2;
197+
const midY = cy + (this._startY + this._endY) / 2;
198+
const apexOffset = this._tieHeight * 0.75;
199+
const apexY = midY + (isDown ? apexOffset : -apexOffset);
200+
const w = canvas.measureText(slurText).width;
201+
const padding = canvas.font.size * 0.35;
202+
const textY = isDown ? apexY + padding : apexY - canvas.font.size - padding;
203+
canvas.fillText(slurText, midX - w / 2, textY);
204+
}
205+
}
206+
207+
protected getSlurText(): string | undefined {
208+
return undefined;
191209
}
192210

193211
protected abstract shouldDrawBendSlur(): boolean;

0 commit comments

Comments
 (0)