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

Commit

Permalink
feat: use Snapshot API instead of cairo
Browse files Browse the repository at this point in the history
  • Loading branch information
vixalien committed Feb 19, 2024
1 parent c0b65e4 commit fc366f9
Showing 1 changed file with 40 additions and 51 deletions.
91 changes: 40 additions & 51 deletions src/waveform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,21 +23,19 @@

import Adw from "gi://Adw";
import GObject from "gi://GObject";
import Gdk from "gi://Gdk?version=4.0";
import Gtk from "gi://Gtk?version=4.0";
import Gst from "gi://Gst";

// @ts-expect-error This module doesn't import nicely
import Cairo from "cairo";
import Graphene from "gi://Graphene";

export enum WaveType {
Recorder,
Player,
}

const GUTTER = 4;
const LINE_WIDTH = 1;

export class APWaveForm extends Gtk.DrawingArea {
export class APWaveForm extends Gtk.Widget {
private _position: number;
private dragGesture?: Gtk.GestureDrag;
private hcId: number;
Expand Down Expand Up @@ -93,8 +91,6 @@ export class APWaveForm extends Gtk.DrawingArea {
this.queue_draw();
},
);

this.set_draw_func(this.drawFunc.bind(this));
}

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

private drawFunc(
_: Gtk.DrawingArea,
ctx: Cairo.Context,
width: number,
height: number,
) {
vfunc_snapshot(snapshot: Gtk.Snapshot): void {
const height = this.get_height();
const width = this.get_width();

const peaks = this.peaks;
const vertiCenter = height / 2;
const horizCenter = width / 2;
Expand All @@ -140,23 +134,17 @@ export class APWaveForm extends Gtk.DrawingArea {

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

// Because the cairo module isn't real, we have to use these to ignore `any`.
// We keep them to the minimum possible scope to catch real errors.
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
ctx.setLineCap(Cairo.LineCap.SQUARE);
ctx.setAntialias(Cairo.Antialias.NONE);
ctx.setLineWidth(2);

this.setSourceRGBA(ctx, leftColor);

ctx.moveTo(horizCenter, vertiCenter - height);
ctx.lineTo(horizCenter, vertiCenter + height);
ctx.stroke();
// Clip the snapshot to the widget area.
// Turns out the DrawingArea was automatically doing that for us
snapshot.push_clip(
new Graphene.Rect({ size: new Graphene.Size({ width, height }) }),
);

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

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

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

if (pointer > horizCenter) {
this.setSourceRGBA(ctx, rightColor);
} else {
this.setSourceRGBA(ctx, leftColor);
}

/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
ctx.moveTo(pointer, vertiCenter + peak * height);
ctx.lineTo(pointer, vertiCenter - peak * height);
ctx.stroke();
/* eslint-enable @typescript-eslint/no-unsafe-call */
/* eslint-enable @typescript-eslint/no-unsafe-member-access */
// only show 70% of the peaks. there are usually few peaks that are
// over 70% high, and those get clipped so that not much space is empty
const line_height = Math.max(peak * height * 0.7, 1);

const line = new Graphene.Rect({
origin: new Graphene.Point({
x: pointer,
y: vertiCenter - line_height,
}),
size: new Graphene.Size({
width: LINE_WIDTH,
height: line_height * 2,
}),
});
snapshot.append_color(
pointer > horizCenter ? rightColor : leftColor,
line,
);

pointer += GUTTER;
}

snapshot.pop();
}

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

private setSourceRGBA(cr: Cairo.Context, rgba: Gdk.RGBA): void {
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
cr.setSourceRGBA(rgba.red, rgba.green, rgba.blue, rgba.alpha);
/* eslint-enable @typescript-eslint/no-unsafe-call */
/* eslint-enable @typescript-eslint/no-unsafe-member-access */
}

public destroy(): void {
Adw.StyleManager.get_default().disconnect(this.hcId);
this.peaks.length = 0;
Expand Down

0 comments on commit fc366f9

Please sign in to comment.