@@ -11,7 +11,7 @@ const Cogl = imports.gi.Cogl;
1111const Gio = imports . gi . Gio ;
1212
1313const UUID = "picture-frames@KopfdesDaemons" ;
14- Gettext . bindtextdomain ( UUID , GLib . get_home_dir ( ) + "/.local/share /locale" ) ;
14+ Gettext . bindtextdomain ( UUID , GLib . get_user_data_dir ( ) + "/locale" ) ;
1515
1616function _ ( str ) {
1717 return Gettext . dgettext ( UUID , str ) ;
@@ -20,34 +20,75 @@ function _(str) {
2020class MyDesklet extends Desklet . Desklet {
2121 constructor ( metadata , deskletId ) {
2222 super ( metadata , deskletId ) ;
23- this . defaultImagePath = this . metadata . path + "/images/default.jpg" ;
23+ this . setHeader ( _ ( "Picture Frame" ) ) ;
24+
25+ this . _defaultImagePath = this . metadata . path + "/images/default.jpg" ;
26+ this . _animationTimeoutId = null ;
27+ this . _isReloading = false ;
28+
29+ // Default settings
30+ this . imagePath = "" ;
31+ this . shape = "wave" ;
32+ this . size = 250 ;
33+ this . showBorder = true ;
34+ this . borderColor = "rgba(33, 170, 70, 0.5)" ;
35+ this . borderWidth = 15 ;
36+ this . wavesNumber = 10 ;
37+ this . waveDepth = 5 ;
38+ this . spikesNumber = 10 ;
39+ this . spikesDepth = 10 ;
40+ this . alignY = 50 ;
41+ this . alignX = 50 ;
2442
2543 // Setup settings and bind them to properties
26- const settings = new Settings . DeskletSettings ( this , metadata [ "uuid" ] , deskletId ) ;
27- settings . bindProperty ( Settings . BindingDirection . IN , "image-path" , "imagePath" , this . _initUI . bind ( this ) ) ;
28- settings . bindProperty ( Settings . BindingDirection . IN , "shape" , "shape" , this . _initUI . bind ( this ) ) ;
29- settings . bindProperty ( Settings . BindingDirection . IN , "size" , "size" , this . _initUI . bind ( this ) ) ;
30- settings . bindProperty ( Settings . BindingDirection . IN , "show-border" , "showBorder" , this . _initUI . bind ( this ) ) ;
31- settings . bindProperty ( Settings . BindingDirection . IN , "border-color" , "borderColor" , this . _initUI . bind ( this ) ) ;
32- settings . bindProperty ( Settings . BindingDirection . IN , "border-width" , "borderWidth" , this . _initUI . bind ( this ) ) ;
33- settings . bindProperty ( Settings . BindingDirection . IN , "waves-number" , "wavesNumber" , this . _initUI . bind ( this ) ) ;
34- settings . bindProperty ( Settings . BindingDirection . IN , "spikes-number" , "spikesNumber" , this . _initUI . bind ( this ) ) ;
35- settings . bindProperty ( Settings . BindingDirection . IN , "wave-depth" , "waveDepth" , this . _initUI . bind ( this ) ) ;
36- settings . bindProperty ( Settings . BindingDirection . IN , "spikes-depth" , "spikesDepth" , this . _initUI . bind ( this ) ) ;
37- settings . bindProperty ( Settings . BindingDirection . IN , "align-x" , "alignX" , this . _initUI . bind ( this ) ) ;
38- settings . bindProperty ( Settings . BindingDirection . IN , "align-y" , "alignY" , this . _initUI . bind ( this ) ) ;
44+ this . settings = new Settings . DeskletSettings ( this , metadata [ "uuid" ] , deskletId ) ;
45+ this . settings . bindProperty ( Settings . BindingDirection . IN , "image-path" , "imagePath" , this . _initUI . bind ( this ) ) ;
46+ this . settings . bindProperty ( Settings . BindingDirection . IN , "shape" , "shape" , this . _initUI . bind ( this ) ) ;
47+ this . settings . bindProperty ( Settings . BindingDirection . IN , "size" , "size" , this . _initUI . bind ( this ) ) ;
48+ this . settings . bindProperty ( Settings . BindingDirection . IN , "show-border" , "showBorder" , this . _initUI . bind ( this ) ) ;
49+ this . settings . bindProperty ( Settings . BindingDirection . IN , "border-color" , "borderColor" , this . _initUI . bind ( this ) ) ;
50+ this . settings . bindProperty ( Settings . BindingDirection . IN , "border-width" , "borderWidth" , this . _initUI . bind ( this ) ) ;
51+ this . settings . bindProperty ( Settings . BindingDirection . IN , "waves-number" , "wavesNumber" , this . _initUI . bind ( this ) ) ;
52+ this . settings . bindProperty ( Settings . BindingDirection . IN , "spikes-number" , "spikesNumber" , this . _initUI . bind ( this ) ) ;
53+ this . settings . bindProperty ( Settings . BindingDirection . IN , "wave-depth" , "waveDepth" , this . _initUI . bind ( this ) ) ;
54+ this . settings . bindProperty ( Settings . BindingDirection . IN , "spikes-depth" , "spikesDepth" , this . _initUI . bind ( this ) ) ;
55+ this . settings . bindProperty ( Settings . BindingDirection . IN , "align-x" , "alignX" , this . _initUI . bind ( this ) ) ;
56+ this . settings . bindProperty ( Settings . BindingDirection . IN , "align-y" , "alignY" , this . _initUI . bind ( this ) ) ;
57+ }
3958
40- this . setHeader ( _ ( "Picture Frame" ) ) ;
59+ on_desklet_added_to_desktop ( ) {
4160 this . _initUI ( ) ;
4261 }
4362
63+ on_desklet_removed ( ) {
64+ if ( this . _animationTimeoutId ) {
65+ GLib . source_remove ( this . _animationTimeoutId ) ;
66+ this . _animationTimeoutId = null ;
67+ }
68+ if ( this . settings && ! this . _isReloading ) {
69+ this . settings . finalize ( ) ;
70+ }
71+ }
72+
73+ on_desklet_reloaded ( ) {
74+ this . _isReloading = true ;
75+ }
76+
4477 _initUI ( ) {
78+ this . _loadId = ( this . _loadId || 0 ) + 1 ;
79+ const currentLoadId = this . _loadId ;
80+
81+ if ( this . _animationTimeoutId ) {
82+ GLib . source_remove ( this . _animationTimeoutId ) ;
83+ this . _animationTimeoutId = null ;
84+ }
85+
4586 const mainContainer = new St . BoxLayout ( { vertical : true } ) ;
46- if ( ! this . imagePath ) this . imagePath = this . defaultImagePath ;
87+ if ( ! this . imagePath ) this . imagePath = this . _defaultImagePath ;
4788
4889 const size = this . size ;
4990 const finalImagePath = decodeURIComponent ( this . imagePath . replace ( "file://" , "" ) ) ;
50- const imageActor = this . _createShapedImageActor ( finalImagePath , size ) ;
91+ const imageActor = this . _createShapedImageActor ( finalImagePath , size , currentLoadId ) ;
5192
5293 mainContainer . add_child ( imageActor ) ;
5394 this . setContent ( mainContainer ) ;
@@ -129,12 +170,12 @@ class MyDesklet extends Desklet.Desklet {
129170 cr . closePath ( ) ;
130171 }
131172
132- _createShapedImageActor ( imagePath , size ) {
173+ _createShapedImageActor ( imagePath , size , loadId ) {
133174 const canvas = new Clutter . Canvas ( ) ;
134175 canvas . set_size ( size , size ) ;
135176 const actor = new Clutter . Actor ( { width : size , height : size , content : canvas } ) ;
136177 const file = Gio . file_new_for_path ( imagePath ) ;
137- let pixbuf = null ;
178+ let iter = null ;
138179
139180 canvas . connect ( "draw" , ( canvas , cr , width , height ) => {
140181 // Clear the canvas
@@ -144,7 +185,7 @@ class MyDesklet extends Desklet.Desklet {
144185 cr . restore ( ) ;
145186 cr . setOperator ( Cairo . Operator . OVER ) ;
146187
147- if ( pixbuf === null ) {
188+ if ( iter === null ) {
148189 // Draw loading text
149190 cr . setSourceRGBA ( 1.0 , 1.0 , 1.0 , 0.7 ) ; // Semi-transparent white
150191 cr . selectFontFace ( "sans-serif" , Cairo . FontSlant . NORMAL , Cairo . FontWeight . NORMAL ) ;
@@ -155,25 +196,51 @@ class MyDesklet extends Desklet.Desklet {
155196 cr . showText ( text ) ;
156197 } else {
157198 // Draw the shaped image once pixbuf is loaded
158- this . _drawFinalImage ( cr , pixbuf , width , height ) ;
199+ const currentPixbuf = iter . get_pixbuf ( ) ;
200+ this . _drawFinalImage ( cr , currentPixbuf , width , height ) ;
159201 }
160202 return true ;
161203 } ) ;
162204 canvas . invalidate ( ) ; // Initial draw with "Loading..."
163205
164206 file . read_async ( GLib . PRIORITY_DEFAULT , null , ( source , res ) => {
207+ // Check if this load operation is still relevant
208+ if ( this . _loadId !== loadId ) return ;
165209 try {
166210 const stream = source . read_finish ( res ) ;
167- GdkPixbuf . Pixbuf . new_from_stream_async ( stream , null , ( source , res ) => {
211+ GdkPixbuf . PixbufAnimation . new_from_stream_async ( stream , null , ( source , res ) => {
212+ if ( this . _loadId !== loadId ) return ;
168213 try {
169- pixbuf = GdkPixbuf . Pixbuf . new_from_stream_finish ( res ) ;
170- canvas . invalidate ( ) ; // Force a redraw now that the pixbuf is loaded
214+ const anim = GdkPixbuf . PixbufAnimation . new_from_stream_finish ( res ) ;
215+ iter = anim . get_iter ( null ) ;
216+
217+ const updateAnimation = ( ) => {
218+ if ( this . _loadId !== loadId ) return ;
219+ if ( this . _animationTimeoutId ) {
220+ GLib . source_remove ( this . _animationTimeoutId ) ;
221+ this . _animationTimeoutId = null ;
222+ }
223+ const delay = iter . get_delay_time ( ) ;
224+ if ( delay >= 0 ) {
225+ // -1 means static image, no animation
226+ this . _animationTimeoutId = GLib . timeout_add ( GLib . PRIORITY_DEFAULT , delay > 0 ? delay : 100 , ( ) => {
227+ if ( this . _loadId !== loadId ) return GLib . SOURCE_REMOVE ;
228+ iter . advance ( null ) ;
229+ canvas . invalidate ( ) ;
230+ updateAnimation ( ) ;
231+ return GLib . SOURCE_REMOVE ;
232+ } ) ;
233+ }
234+ } ;
235+
236+ canvas . invalidate ( ) ;
237+ updateAnimation ( ) ;
171238 } catch ( e ) {
172- global . logError ( `Error creating pixbuf from stream: ${ e } ` ) ;
239+ global . logError ( `${ UUID } : Error creating pixbuf animation from stream: ${ e } ` ) ;
173240 }
174241 } ) ;
175242 } catch ( e ) {
176- global . logError ( `Error reading file async: ${ e } ` ) ;
243+ global . logError ( `${ UUID } : Error reading file async: ${ e } ` ) ;
177244 }
178245 } ) ;
179246 return actor ;
@@ -197,16 +264,17 @@ class MyDesklet extends Desklet.Desklet {
197264 newHeight = width / aspect ;
198265 }
199266
200- const scaledPixbuf = pixbuf . scale_simple ( newWidth , newHeight , GdkPixbuf . InterpType . BILINEAR ) ;
201- const pixbufWithAlpha = scaledPixbuf . add_alpha ( false , 0 , 0 , 0 ) ;
202-
203267 cr . save ( ) ;
204268 this . _drawShapePath ( cr , this . shape , width / 2 , height / 2 , width / 2 ) ;
205269 cr . clip ( ) ;
206270
207271 const drawX = ( width - newWidth ) * ( this . alignX / 100 ) ;
208272 const drawY = ( height - newHeight ) * ( this . alignY / 100 ) ;
209- Gdk . cairo_set_source_pixbuf ( cr , pixbufWithAlpha , drawX , drawY ) ;
273+
274+ cr . translate ( drawX , drawY ) ;
275+ cr . scale ( newWidth / originalWidth , newHeight / originalHeight ) ;
276+
277+ Gdk . cairo_set_source_pixbuf ( cr , pixbuf , 0 , 0 ) ;
210278 cr . paint ( ) ;
211279 cr . restore ( ) ;
212280
@@ -225,7 +293,7 @@ class MyDesklet extends Desklet.Desklet {
225293 cr . stroke ( ) ;
226294 }
227295 } catch ( e ) {
228- global . logError ( `Error drawing shaped image: ${ e } ` ) ;
296+ global . logError ( `${ UUID } : Error drawing shaped image: ${ e } ` ) ;
229297 }
230298 }
231299
@@ -247,7 +315,7 @@ class MyDesklet extends Desklet.Desklet {
247315 const pixBuf = GdkPixbuf . Pixbuf . new_from_file_at_size ( imageFileName , requestedWidth , requestedHeight ) ;
248316 return this . _createActorFromPixbuf ( pixBuf ) ;
249317 } catch ( e ) {
250- global . logError ( `Error loading image ${ imageFileName } : ${ e } ` ) ;
318+ global . logError ( `${ UUID } : Error loading image ${ imageFileName } : ${ e } ` ) ;
251319 return new St . Label ( { text : "Error" + e . message , style_class : "picture-frame-error-label" } ) ;
252320 }
253321 }
0 commit comments