Skip to content
This repository was archived by the owner on Mar 11, 2024. It is now read-only.

Commit be9200c

Browse files
committed
feat: use Snapshot API instead of cairo
1 parent 23c5de9 commit be9200c

File tree

1 file changed

+40
-51
lines changed

1 file changed

+40
-51
lines changed

src/waveform.ts

+40-51
Original file line numberDiff line numberDiff line change
@@ -23,21 +23,19 @@
2323

2424
import Adw from "gi://Adw";
2525
import GObject from "gi://GObject";
26-
import Gdk from "gi://Gdk?version=4.0";
2726
import Gtk from "gi://Gtk?version=4.0";
2827
import Gst from "gi://Gst";
29-
30-
// @ts-expect-error This module doesn't import nicely
31-
import Cairo from "cairo";
28+
import Graphene from "gi://Graphene";
3229

3330
export enum WaveType {
3431
Recorder,
3532
Player,
3633
}
3734

3835
const GUTTER = 4;
36+
const LINE_WIDTH = 1;
3937

40-
export class APWaveForm extends Gtk.DrawingArea {
38+
export class APWaveForm extends Gtk.Widget {
4139
private _position: number;
4240
private dragGesture?: Gtk.GestureDrag;
4341
private hcId: number;
@@ -93,8 +91,6 @@ export class APWaveForm extends Gtk.DrawingArea {
9391
this.queue_draw();
9492
},
9593
);
96-
97-
this.set_draw_func(this.drawFunc.bind(this));
9894
}
9995

10096
get peaks(): number[] {
@@ -122,12 +118,10 @@ export class APWaveForm extends Gtk.DrawingArea {
122118
this.emit("position-changed", this.position);
123119
}
124120

125-
private drawFunc(
126-
_: Gtk.DrawingArea,
127-
ctx: Cairo.Context,
128-
width: number,
129-
height: number,
130-
) {
121+
vfunc_snapshot(snapshot: Gtk.Snapshot): void {
122+
const height = this.get_height();
123+
const width = this.get_width();
124+
131125
const peaks = this.peaks;
132126
const vertiCenter = height / 2;
133127
const horizCenter = width / 2;
@@ -140,23 +134,17 @@ export class APWaveForm extends Gtk.DrawingArea {
140134

141135
const leftColor = this.safeLookupColor("accent_color");
142136

143-
// Because the cairo module isn't real, we have to use these to ignore `any`.
144-
// We keep them to the minimum possible scope to catch real errors.
145-
/* eslint-disable @typescript-eslint/no-unsafe-call */
146-
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
147-
ctx.setLineCap(Cairo.LineCap.SQUARE);
148-
ctx.setAntialias(Cairo.Antialias.NONE);
149-
ctx.setLineWidth(2);
150-
151-
this.setSourceRGBA(ctx, leftColor);
152-
153-
ctx.moveTo(horizCenter, vertiCenter - height);
154-
ctx.lineTo(horizCenter, vertiCenter + height);
155-
ctx.stroke();
137+
// Clip the snapshot to the widget area.
138+
// Turns out the DrawingArea was automatically doing that for us
139+
snapshot.push_clip(
140+
new Graphene.Rect({ size: new Graphene.Size({ width, height }) }),
141+
);
156142

157-
ctx.setLineWidth(2);
158-
/* eslint-enable @typescript-eslint/no-unsafe-call */
159-
/* eslint-enable @typescript-eslint/no-unsafe-member-access */
143+
const indicator = new Graphene.Rect({
144+
origin: new Graphene.Point({ x: horizCenter, y: 0 }),
145+
size: new Graphene.Size({ width: LINE_WIDTH, height }),
146+
});
147+
snapshot.append_color(leftColor, indicator);
160148

161149
// only draw the waveform for peaks inside the view
162150
let invisible_peaks = 0;
@@ -166,7 +154,9 @@ export class APWaveForm extends Gtk.DrawingArea {
166154
pointer = pointer + invisible_peaks;
167155
}
168156

169-
for (const peak of peaks.slice(invisible_peaks / GUTTER)) {
157+
// eslint-disable-next-line @typescript-eslint/no-for-in-array
158+
for (const id in peaks.slice(invisible_peaks / GUTTER)) {
159+
const peak = peaks.slice(invisible_peaks / GUTTER)[id];
170160
// this shouldn't happen, but just in case
171161
if (pointer < 0) {
172162
pointer += GUTTER;
@@ -177,22 +167,29 @@ export class APWaveForm extends Gtk.DrawingArea {
177167
break;
178168
}
179169

180-
if (pointer > horizCenter) {
181-
this.setSourceRGBA(ctx, rightColor);
182-
} else {
183-
this.setSourceRGBA(ctx, leftColor);
184-
}
185-
186-
/* eslint-disable @typescript-eslint/no-unsafe-call */
187-
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
188-
ctx.moveTo(pointer, vertiCenter + peak * height);
189-
ctx.lineTo(pointer, vertiCenter - peak * height);
190-
ctx.stroke();
191-
/* eslint-enable @typescript-eslint/no-unsafe-call */
192-
/* eslint-enable @typescript-eslint/no-unsafe-member-access */
170+
// only show 70% of the peaks. there are usually few peaks that are
171+
// over 70% high, and those get clipped so that not much space is empty
172+
const line_height = Math.max(peak * height * 0.7, 1);
173+
174+
const line = new Graphene.Rect({
175+
origin: new Graphene.Point({
176+
x: pointer,
177+
y: vertiCenter - line_height,
178+
}),
179+
size: new Graphene.Size({
180+
width: LINE_WIDTH,
181+
height: line_height * 2,
182+
}),
183+
});
184+
snapshot.append_color(
185+
pointer > horizCenter ? rightColor : leftColor,
186+
line,
187+
);
193188

194189
pointer += GUTTER;
195190
}
191+
192+
snapshot.pop();
196193
}
197194

198195
set position(pos: number) {
@@ -207,14 +204,6 @@ export class APWaveForm extends Gtk.DrawingArea {
207204
return this._position;
208205
}
209206

210-
private setSourceRGBA(cr: Cairo.Context, rgba: Gdk.RGBA): void {
211-
/* eslint-disable @typescript-eslint/no-unsafe-call */
212-
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
213-
cr.setSourceRGBA(rgba.red, rgba.green, rgba.blue, rgba.alpha);
214-
/* eslint-enable @typescript-eslint/no-unsafe-call */
215-
/* eslint-enable @typescript-eslint/no-unsafe-member-access */
216-
}
217-
218207
public destroy(): void {
219208
Adw.StyleManager.get_default().disconnect(this.hcId);
220209
this.peaks.length = 0;

0 commit comments

Comments
 (0)