Skip to content

Commit 76ff335

Browse files
committed
Bug fixes
- Selection overlays no maintain their correct size, even when the arrangement viewer is scaled. - When a new score is loaded, update its stats in the status bar. Signed-off-by: Mike Lischke <mike@lischke-online.de>
1 parent 084b2c1 commit 76ff335

2 files changed

Lines changed: 62 additions & 27 deletions

File tree

src/App.tsx

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1201,6 +1201,11 @@ export class App extends UIComponent<{}, IAppState> {
12011201
);
12021202

12031203
this.forceUpdate();
1204+
1205+
const { phase } = this.state;
1206+
if (phase === AppPhase.Running) {
1207+
this.updateStatsItem();
1208+
}
12041209
}
12051210

12061211
private initEventHandlers(): void {
@@ -1328,12 +1333,18 @@ export class App extends UIComponent<{}, IAppState> {
13281333
`${Math.round(100 * metrics.realTimeLength) / 100} s`;
13291334

13301335
if (!this.statsItem) {
1331-
this.statsItem = Statusbar.createStatusBarItem({
1332-
id: "scoreStats",
1333-
text,
1334-
alignment: StatusBarAlignment.Right,
1335-
priority: 10,
1336-
});
1336+
try {
1337+
this.statsItem = Statusbar.createStatusBarItem({
1338+
id: "scoreStats",
1339+
text,
1340+
alignment: StatusBarAlignment.Right,
1341+
priority: 10,
1342+
});
1343+
} catch {
1344+
// Statusbar is not mounted yet — the item will be created on
1345+
// the next updateStatsItem call (e.g. when timeParamsChanged fires).
1346+
return;
1347+
}
13371348
} else {
13381349
this.statsItem.text = text;
13391350
}

src/ui/SelectionView.ts

Lines changed: 45 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,14 @@ export class SelectionView {
3232
private startX = 0;
3333
private startY = 0;
3434

35+
/**
36+
* Ratio of viewport pixels to CSS pixels inside #trackViewerContainer.
37+
* Viewport-pixel deltas from getBoundingClientRect() are divided by this
38+
* factor to obtain CSS-pixel values for overlay positioning. CSS-pixel
39+
* constants (offsetY, heightOffset, etc.) are never divided.
40+
*/
41+
private zoomFactor = 1;
42+
3543
public constructor(private manager: SelectionManager, private eventContainer: HTMLElement) {
3644
requisitions.register("selectionChanged", this.handleSelectionChanged);
3745
eventContainer.addEventListener("pointerdown", this.handlePointerDown);
@@ -224,6 +232,13 @@ export class SelectionView {
224232

225233
const containerRect = overlayContainer.getBoundingClientRect();
226234

235+
// Measure the actual zoom factor from the DOM: offsetWidth gives CSS pixels,
236+
// getBoundingClientRect().width gives viewport pixels (post-zoom). The ratio is
237+
// the effective zoom factor, which is more reliable than parsing style.zoom.
238+
const cssW = contentHost.offsetWidth;
239+
const viewportW = contentHost.getBoundingClientRect().width;
240+
this.zoomFactor = cssW > 0 ? viewportW / cssW : 1;
241+
227242
// Separate entries by granularity.
228243
const trackEntries: ISelectionEntry[] = [];
229244
const measureEntries: ISelectionEntry[] = [];
@@ -399,21 +414,25 @@ export class SelectionView {
399414
const firstContent = runs[0].querySelector<HTMLElement>(contentSelector);
400415
if (firstContent) {
401416
const firstRect = firstContent.getBoundingClientRect();
402-
minLeft = Math.max(minLeft, firstRect.left - 10);
417+
minLeft = Math.max(minLeft, firstRect.left - (10 * this.zoomFactor));
403418
}
404419

405420
// Narrow right edge to the last run's inner content.
406421
const lastContent = runs[runs.length - 1].querySelector<HTMLElement>(contentSelector);
407422
if (lastContent) {
408423
const lastRect = lastContent.getBoundingClientRect();
409-
maxRight = lastRect.right + 2;
424+
maxRight = lastRect.right + (2 * this.zoomFactor);
410425
}
411426

427+
// Convert viewport-pixel deltas to CSS pixels. offsetY/heightOffset
428+
// are CSS pixels and must not be divided.
429+
const z = this.zoomFactor;
430+
412431
this.createOverlay(overlayContainer, {
413-
x: minLeft - containerRect.left,
414-
y: minTop - containerRect.top + offsetY,
415-
width: maxRight - minLeft,
416-
height: maxBottom - minTop + heightOffset,
432+
x: (minLeft - containerRect.left) / z,
433+
y: ((minTop - containerRect.top) / z) + offsetY,
434+
width: (maxRight - minLeft) / z,
435+
height: ((maxBottom - minTop) / z) + heightOffset,
417436
});
418437
}
419438

@@ -740,7 +759,8 @@ export class SelectionView {
740759

741760
const rect = this.computeMergedRect(contentHost, selectors.join(","), containerRect);
742761
if (rect) {
743-
rect.y -= 10;
762+
rect.x += 4;
763+
rect.width -= 8;
744764
this.createOverlay(overlayContainer, rect);
745765
}
746766
}
@@ -778,13 +798,15 @@ export class SelectionView {
778798
}
779799
}
780800

781-
const padding = 2; // small visual breathing room on each side
801+
// Convert viewport-pixel deltas to CSS pixels. offsetY/heightOffset are
802+
// already CSS pixels and must not be divided.
803+
const z = this.zoomFactor;
782804

783805
this.createOverlay(overlayContainer, {
784-
x: minLeft - containerRect.left - padding,
785-
y: minTop - containerRect.top + offsetY,
786-
width: (maxRight - minLeft) + (2 * padding),
787-
height: maxBottom - minTop + heightOffset,
806+
x: (minLeft - containerRect.left) / z,
807+
y: ((minTop - containerRect.top) / z) + offsetY,
808+
width: (maxRight - minLeft) / z,
809+
height: ((maxBottom - minTop) / z) + heightOffset,
788810
});
789811
}
790812

@@ -805,11 +827,13 @@ export class SelectionView {
805827
const marginBottom = parseFloat(style.marginBottom) || 0;
806828
const marginLeft = parseFloat(style.marginLeft) || 0;
807829

830+
// All values in viewport pixels for consistent min/max computation.
831+
// Callers convert to CSS pixels before passing to createOverlay.
808832
return {
809-
x: elRect.left - marginLeft - containerRect.left,
810-
y: elRect.top - marginTop - containerRect.top,
811-
width: elRect.width + marginLeft + marginRight,
812-
height: elRect.height + marginTop + marginBottom,
833+
x: elRect.left - (marginLeft * this.zoomFactor) - containerRect.left,
834+
y: elRect.top - (marginTop * this.zoomFactor) - containerRect.top,
835+
width: elRect.width + ((marginLeft + marginRight) * this.zoomFactor),
836+
height: elRect.height + ((marginTop + marginBottom) * this.zoomFactor),
813837
};
814838
}
815839

@@ -864,13 +888,13 @@ export class SelectionView {
864888
}
865889
}
866890

867-
const padding = 2;
891+
const z = this.zoomFactor;
868892

869893
return {
870-
x: minLeft - containerRect.left - padding,
871-
y: minTop - containerRect.top,
872-
width: (maxRight - minLeft) + (2 * padding),
873-
height: maxBottom - minTop,
894+
x: (minLeft - containerRect.left) / z,
895+
y: (minTop - containerRect.top) / z,
896+
width: (maxRight - minLeft) / z,
897+
height: (maxBottom - minTop) / z,
874898
};
875899
}
876900

0 commit comments

Comments
 (0)