use |yield| in addition to nested timers for ChatStatusIcon fading

http://syzygy.st/javascript-coroutines/ is worth mentioning
This commit is contained in:
foudfou 2013-05-01 15:44:52 +02:00
parent 5882e3b9c5
commit 2e064cae99
1 changed files with 75 additions and 66 deletions

View File

@ -93,14 +93,6 @@ firetray.ChatStatusIcon = {
gtk.gtk_status_icon_set_from_pixbuf(this.trayIcon, null);
},
/**
* EXPERIMENTAL fancy blinking.
* TODO: how to wait for last fade in to restore themedIconNameCurrent
*/
startCrossFade: function() {
this.crossFade(this.buildPixBuf());
},
buildPixBuf: function() {
let icon_theme = gtk.gtk_icon_theme_get_for_screen(gdk.gdk_screen_get_default());
@ -126,15 +118,10 @@ firetray.ChatStatusIcon = {
if (n_channels != 4)
log.error("wrong nb of channels for pixbuf");
return pixbuf; // TO BE UNREFED WITH to g_object_unref() !!
},
crossFade: function(pixbuf) {
// init transform
let width = gdk.gdk_pixbuf_get_width(pixbuf);
let height = gdk.gdk_pixbuf_get_height(pixbuf);
log.debug("width="+width+", height="+height);
let n_channels = gdk.gdk_pixbuf_get_n_channels(pixbuf);
let length = width*height*n_channels;
let pixels = ctypes.cast(gdk.gdk_pixbuf_get_pixels(pixbuf),
gobject.guchar.array(length).ptr);
@ -146,63 +133,85 @@ firetray.ChatStatusIcon = {
for (let i=3; i<length; i+=n_channels)
alpha_bak[(i-3)/n_channels] = pixels.contents[i];
const ALPHA_STEP = 5;
let timers = [];
let ret = {
pixbuf: pixbuf, // TO BE UNREFED WITH to g_object_unref() !!
width: width,
height: height,
length: length,
n_channels: n_channels,
pixels: pixels,
buffer: buffer,
alpha_bak: alpha_bak
};
function fadeIn(alpha) {
for(let i=3; i<length; i+=n_channels)
if (pixels.contents[i]+ALPHA_STEP<=alpha_bak[(i-3)/n_channels]) {
pixels.contents[i] += ALPHA_STEP;
}
log.info("gtk_status_icon_set_from_pixbuf="+pixbuf);
gtk.gtk_status_icon_set_from_pixbuf(firetray.ChatStatusIcon.trayIcon, pixbuf);
if (alpha < 255) {
alpha += ALPHA_STEP;
timers.push(firetray.Utils.timer(10, Ci.nsITimer.TYPE_ONE_SHOT,
function(){fadeIn(alpha);}));
} else {
if (firetray.ChatStatusIcon.events['stop-cross-fade']) {
delete firetray.ChatStatusIcon.events['stop-cross-fade'];
firetray.ChatStatusIcon.setIconImage(firetray.ChatStatusIcon.themedIconNameCurrent);
return;
} else if (firetray.ChatStatusIcon.events['icon-changed']) {
delete firetray.ChatStatusIcon.events['icon-changed'];
firetray.ChatStatusIcon.crossFade(firetray.ChatStatusIcon.buildPixBuf());
return;
} else {
timers.push(firetray.Utils.timer(500, Ci.nsITimer.TYPE_ONE_SHOT,
function(){fadeOut(255);}));
}
}
}
function fadeOut(alpha) {
for(let i=3; i<length; i+=n_channels)
if (pixels.contents[i]-ALPHA_STEP>0)
pixels.contents[i] -= ALPHA_STEP;
log.info("gtk_status_icon_set_from_pixbuf="+pixbuf);
gtk.gtk_status_icon_set_from_pixbuf(firetray.ChatStatusIcon.trayIcon, pixbuf);
if (alpha > 0) {
alpha -= ALPHA_STEP;
timers.push(firetray.Utils.timer(10, Ci.nsITimer.TYPE_ONE_SHOT,
function(){fadeOut(alpha);}));
} else {
fadeIn(0);
}
}
fadeOut(255);
gobject.g_object_unref(pixbuf); // FIXME: not sure if this shouldn't be done at 'stop-cross-fade'
return ret;
},
dropPixBuf: function(p) {
gobject.g_object_unref(p.pixbuf); // FIXME: not sure if this shouldn't be done at 'stop-cross-fade'
log.info("pixbuf unref'd");
},
startCrossFade: function() { // TODO: Foudil: rename to startFading
const ALPHA_STEP = 5;
const ALPHA_STEP_SLEEP_MILLISECONDS = 10;
const FADE_OVER_SLEEP_MILLISECONDS = 500;
function fadeGen(p) {
for (let a=255; a>0; a-=ALPHA_STEP) {
for(let i=3; i<p.length; i+=p.n_channels)
if (p.pixels.contents[i]-ALPHA_STEP>0)
p.pixels.contents[i] -= ALPHA_STEP;
gtk.gtk_status_icon_set_from_pixbuf(firetray.ChatStatusIcon.trayIcon, p.pixbuf);
yield true;
}
for (let a=255; a>0; a-=ALPHA_STEP) {
for(let i=3; i<p.length; i+=p.n_channels)
if (p.pixels.contents[i]+ALPHA_STEP<=p.alpha_bak[(i-3)/p.n_channels]) {
p.pixels.contents[i] += ALPHA_STEP;
}
gtk.gtk_status_icon_set_from_pixbuf(firetray.ChatStatusIcon.trayIcon, p.pixbuf);
yield true;
}
}
function fadeLoop(p) {
let fadeOutfadeIn = fadeGen(p);
(function step() {
try {
if (fadeOutfadeIn.next())
firetray.Utils.timer(ALPHA_STEP_SLEEP_MILLISECONDS,
Ci.nsITimer.TYPE_ONE_SHOT, step);
} catch (e if e instanceof StopIteration) {
if (firetray.ChatStatusIcon.events['stop-cross-fade']) {
delete firetray.ChatStatusIcon.events['stop-cross-fade'];
firetray.ChatStatusIcon.setIconImage(firetray.ChatStatusIcon.themedIconNameCurrent);
return;
}
if (firetray.ChatStatusIcon.events['icon-changed']) {
delete firetray.ChatStatusIcon.events['icon-changed'];
firetray.ChatStatusIcon.dropPixBuf(p);
let newPixbufObj = firetray.ChatStatusIcon.buildPixBuf();
firetray.Utils.timer(FADE_OVER_SLEEP_MILLISECONDS,
Ci.nsITimer.TYPE_ONE_SHOT, function(){fadeLoop(newPixbufObj);});
} else {
firetray.Utils.timer(FADE_OVER_SLEEP_MILLISECONDS,
Ci.nsITimer.TYPE_ONE_SHOT, function(){fadeLoop(p);});
}
};
})();
}
let pixbufObj = this.buildPixBuf();
fadeLoop(pixbufObj);
firetray.ChatStatusIcon.dropPixBuf(pixbufObj);
},
startIconBlinking: function() { // gtk_status_icon_set_blinking() deprecated
this.on = true;
firetray.ChatStatusIcon.timers['blink'] = firetray.Utils.timer(