2323
2424import Adw from "gi://Adw" ;
2525import GObject from "gi://GObject" ;
26- import Gdk from "gi://Gdk?version=4.0" ;
2726import Gtk from "gi://Gtk?version=4.0" ;
2827import 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
3330export enum WaveType {
3431 Recorder ,
3532 Player ,
3633}
3734
3835const 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